Vue.js explained through pokemon #1 Single file components
Recently I came across an article called Vue.js Pokemon Battle Tutorial and felt all kinds of nostalgia.
After that I was surprised how fast I read through it and how easy it was to actually rebuild a pokemon battle stage.
It got me to a point where I thought to myself, I could build on this tutorial to touch on other facets of Vue.js that were left uncovered.
This is going to be a multipart series that will range from shifting to a component-based approach to little, more general, tidbits like using animations and promises.
Note I will be using short-hand syntax for things as event and property binding. It’s also advised to go through the previous tutorial since it explains the basics.
This series is meant for people who do have a basic understanding of Vue js.
Single file components
In this first article I wanted to talk about using single file .vue components.
The attack-feature will be the next article, our main focus here separating style and data.
In short single file components are .vue files, these normally consist of 3 parts;
a <template> tag that encapsulates the component’s HTML
a <style> tag where you can specify styles that get injected into your page.
a <script> tag where you define your component’s logic (local Vue instance).
Splitting into components
Why would you want to split up your app in components?
The short answer is because it allows you to organise your code more clearly. It isolates data (state), html (template) and even styles.
In the simplest manner we could separate the app into 3 parts, 2 components:
1. Battle stage
2. Opponent (pokemon)
3. Player (pokemon).
The main component will house the main styling, the functionality for the menu, the app’s data and it will act like the glue for its 2 child components (pokemon).
Each pokemon component will house the pokemon’s image, HP bar and attack functionality.
Let’s start by setting up a new project, we’ll be using vue-cli to do the hard work for us.
It’s a tool that scaffolds boilerplate code, saving you a lot of time on setup.
There are a lot of project templates you can choose from (you can check official ones here).
The one we’ll be using for this project is called webpack-simple. It sets up the project with webpack, Vue and and vue-loader. That last package is responsible for parsing the .vue files into a useable format.
Start off by installing vue-cli globally with npm, next create a new project based on the webpack-simple template, lastly cd into the project and install the dependencies.
Optionally as a last step you can run the compilation and open the project in a browser screen.
Alright, you’ll notice a few files, what we’re interested in is the src/ folder.
This is where all the code will live.
We won’t have to touch main.js for now, as it stands it’s loading Vue.js and the App.vue component that came with the boiler plate.
Defining the battle stage
We’ll be using the CSS provided by Michael Mangialardi’s previous tutorial.
Let’s open up src/App.vue and replace the style tag with what you can find here.
That’s all for styling, let’s talk data.
We’ll want to work with a few things:
This key will contain the 2 pokemon: opponent & player,
each pokemon will have a name, image, HP, level and a list of attacks.
Just a string that tells the user what’s happening.
The main menu options
These are the ‘main’ options (fight, pokemon, item bag and run).
Which menu is being shown
We’ll be making use of 3 actual menu’s, the main (options)menu, the battle option menu that lists the player’s pokemon’s attacks and the end of battle menu, asking if the user wants to give it another go.
Within the script tag of App.vue We’ll place our data function.
A small reminder, as opposed to a normal Vue instance definition, we’ll have to return an object from the data function. Normally data is a property within the Vue instance.
By returning the data object from a function we’re telling Vue that this data is meant for only this instance of this component. If we had multiple instances they’d all share the same data, that wouldn’t be practical and kind of beat the point of having components. Isolated state is what we’re after.
Next we’ll start building our stage’s HTML within the template tag of the file. We won’t be showing any Pokemon just yet, we’ll first focus on showing the basic interface:
Main options menu
As you can see, 4 menu items are defined. We’ll have to define a click event handler to make them work.
Let’s create our first method, define a methods property right after the data function and add the ProcessOption method to it.
Fight option menu
This menu will show the player’s pokemon’s attacks. The way they’re currently set up is a little bit different from the original tutorial, we’ll have to retrieve the keys of the player’s pokemon to display them.
We can do this in 2 ways:
- Once the component’s been mounted we could grab the keys and assign them to a data property.
- We could use a computed property to return the keys (which could come in handy if you decide to build the switch-pokemon feature).
Let’s do the latter. Computed properties can be accessed in the same way your data properties are accessed. The main difference is that these can not be set manually, they usually depend on one or more data properties that have a need to be transformed (think of filtered lists as an example).
Let’s add it by defining a computed property between you data function and methods property on the Vue instance. To the computed property we’ll add a method called fightOptions:
We’ll still have to define a method processAttack, but we can’t do that just yet. First we’ll need to define our pokemon components. We’ll come back to this in the next article.
End options menu
Our last menu, this is shown once a pokemon has fainted.
If the player wants to have another go we’ll have to reset both pokemon’s HP, reset the battle text and go back to the main menu.
We’ll have to add a resetBattle method after our processOption method:
That’s it for the stage component for now, it’s time for our next component.
This component is where we’ll see the most code-clean up.
Let’s think about this, the pokemon component should render the pokemon’s-specific UI (meaning the pokemon’s image, name, level and HP bar) as well as handle the pokemon’s attack.
We’ll have to define a few properties so we can make use of the pokemon data, the position on the screen and which type it is (player or opponent).
Next to that we’ll need to define some data properties that assign classes to the main box, hp box and hp bar, defines the pokemon’s image element id and we’ll need a property that defines the inverse type (for the player’s pokemon this would be opponent for example).
We’ll also have to add a few computed properties to get the alive status of the pokemon, as well as one that controls the HP bar style (color and width).
Create a new .vue file
Let’s start off by creating a new .vue file called Pokemon.vue and define our template:
In the next few steps we’ll go over all the variables that are being assigned in the template.
Let’s open a script tag and start our Vue instance definition.
Within the data function we’ll mostly be returning the ‘style identifiers’, meaning classes and id. Next to that we’ll set the reverse type.
Both the this.position and this.type variables are actually props being referenced.
Components can be assigned properties (props for short), think of these as state that’s being passed down from a parent. If any changes are made to this data, this component will be get them and can act accordingly. These are accessible in the same way data and computed properties can be called.
For the pokemon data we’ll want to use a dynamic prop (meaning that it should be passed while prefixed with v-bind: or simply :), the position (top or bottom) and type (player or opponent) prop can just be literal strings since they won’t change.
This has no effect on how you define them in your component, just something to note when actually referencing the component (we’ll see that later on).
The last part of our data, we’ll want 2 computed properties that change automatically.
We want to define the HP bar’s style:
- Calculate the HTML element’s width
- Change the background color when HP drops below a certain point.
We’ll also want an alive property that checks if the pokemon’s HP is above 0.
Let’s add it after the data method on the Vue instance definition:
Alright, we’re there, all data and styling has been defined. Let’s add our component to the stage.
Back to App.vue
Now that our pokemon component is ready to use we’ll have to import it into App.vue, define them in the Vue instance definition and reference it in the template tag.
Let’s replace export default in the script tag with this:
Now we can reference the Pokemon component through a <pokemon> tag in our template.
Let’s add them for our player and opponent pokemon at the top of the template, right after div.battle-scene:
Remember the props?
We’re assigning the pokemon data as a dynamic prop, so the component gets updates to that data. Instead of just : you could prefix it with v-bind:.
The position and type data are meant for styling and context respectively.
But wait, what’s that ref attribute doing there?
I’ve added it in preparation for the next article, by assigning a ref attribute you are essentially naming the child component.
When you do this you can actually access the component’s Vue instance through this.$refs in App.vue.
These are non-reactive, however, they are useful for calling any methods on that Vue instance.
Building the project
All that’s left to do is build the project from the command line.
npm run dev
You should have a working copy now.
I hope you’ve learned some new things about single file components.
In the next article we’ll add the attack functionality back based on Promises and explore a second approach using a simple Event Bus.
You can always find the latest version of the code on github:
learning-vue-through-pokemon - This repo contains the code for my tutorial series 'Learning Vue.js through pokemon'github.com
Or code specific to this article: