Hands-on Vue.js for Beginners (Part 5) 
Tags: vue, javascript, beginners, tutorial
Published: 2019-02-22 17:59:01

This time around we're going to look (finally) at components! So get some ☕️ and let's get started.

Here's the clean slate for today's article 😁

```js
<html>

<head>
  <title>Vue 101</title>
</head>

<body>
  <div id="app">
    
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

  <script>
    const app = new Vue({
      el: '#app',
      data: {
        
      }
    });
  </script>
</body>

</html>
```

# Component Basics

We're going to nosedive right into component creation because this is where really fun stuff starts. But rest assured that this is only barely brushing the power of components. Also, we still have yet to learn `computed` properties and `watchers`, which will also be a huge aid. But we'll cover that next time on top of what we learn today.

Components are a core part of the **Vue.js** framework, they are your Lego blocks for building reactive, rich applications - and luckily for us, they are super simple to learn and use!

Think of a component as any element in your webpage/app that you want to use one or several times. It can be as _smol_ as a button, or input, or as a big as your whole menu bar or even the whole page's view.

The advantage of creating a component is that you write the logic for how its displayed (HTML/CSS), and how it interacts with the user (JS) once - and then you just use it all over your app.

As usual, we're going to start with the simplest possible example, a button. 
Let's define first the **blueprint** or **template** for this component, we'll call it `awesome-button` and it shall be so!

Let's define a new `awesome-button` component. Copy this code above your `new Vue` declaration.

```js
Vue.component('awesome-button', {
  template: `<button @click="clickHandler">Click me for some awesomeness</button>`,
  methods: {
    clickHandler() {
      alert('YAAAS 😎');
    }
  }
});
```

The `Vue` object that is made available to us through the `Vue.js` script tag that we added on day 1 has this `component` method that we are calling here. It allows us to create a new component, as expected. The first parameter we are setting is a *String*, which will be the name of our component.

The second parameter is a JavaScript `object`, which - surprise - is actually the same type of configuration object that we have been using for our main `Vue instance`! What does this mean for you? That you already know how to assign properties and methods for this new component.

In the example above, you will notice just one difference - the `template` property. In here we're declaring a **String** (note that we're also using the **back-tick** character to wrap it so that we can declare multiple lines later on if we need without having to concatenate multiple strings), this **String** will hold our actual HTML code for `this` **component**. In the case of this particular example, just a simple `<button>` tag will suffice.

If you reload your page right now, nothing will happen. Remember earlier I told you this was only the blueprint/template? It's time to actually render it on our page.

Head over to the `<div id="app">` where we have been placing all our markup, and create a new `<awesome-button>` element.

Your HTML should now look something like this:

```html
<div id="app">
  <awesome-button></awesome-button>
</div>
```

Load the page, and now you will actually see the button is rendered on the page. Place a couple, or ten more (reader's choice), `awesome-button` tag on the page. Now you start to see the power of components, although at this point I think we can take it up a nudge.

_Bonus:_ If you're the curious type, take a look at your `page source` and compare it to the `inspect` feature with your browser's developer tools. When the page load, *Vue.js* is using the `<awesome-button>` tag as a placeholder to where it should put the parsed content of our template.

# Level 2 - Something a little more useful

Let's revisit our example from last week, and play some more with our `games` data.

First, let's re-add this array of games into our `data()` in the main **Vue** instance.

```js
const app = new Vue({
  el: '#app',
  data: {
    games: [
      { name: 'Super Mario 64', console: 'Nintendo 64', rating: 4 },
      { name: 'The Legend of Zelda Ocarina of Time', console: 'Nintendo 64', rating: 5 },
      { name: 'Secret of Mana', console: 'Super Nintendo', rating: 4 },
      { name: 'Fallout 76', console: 'Multiple', rating: 1 },
      { name: 'Super Metroid', console: 'Super Nintendo', rating: 6 }
    ]
  }
});
```

Just as before, feel free to update these with your favorite titles.

This time, however, we are going to create a `game-card` component, that will make a bit more sense to display our data in.

Ready for this?

```js
Vue.component('game-card', {
  props: ['gameData'],
  template: `
    <div style="border-radius: .25rem; border: 1px solid #ECECEC; width: 400px; margin: 1rem; padding: 1rem;">
      <h2>{{ gameData.name }} - <small>{{ gameData.console }}</small></h2>

      <span v-for="heart in gameData.rating">❤️</span>

      <button @click="increaseRating">Increase Rating</button>
    </div>
  `,
  methods: {
    increaseRating() {
      // this.game.rating++ ?
    }
  }
});
```

Don't get overwhelmed, you already know all/most-all of this 😃!

We're creating a new `Vue.component` and naming it `game-card`. Let's skip `props` for a second, and look at the `template`.

Nothing new here, except you may have noticed we're accessing the properties of a `gameData` property that is not defined inside `data`, but inside the `props` property. 

Afterward, we declare our `methods` object, with the `increaseRating` method inside it. I've purposely commented out `this.game.rating++` which would be how you may want to address this particular function, but it won't work! Now it's time to talk about `props`.

# Component Props

One of the reserved properties we can have on our custom components is called `props`. In its simplest form, it will take an array of **Strings** that will define variables. In our previous example, we are telling the component blueprint/template that we want it to be aware of a property called `game`.

`Props` will allow us to *pass* information into our components from outside! Let's view this in action, it'll be easier to grasp.

First, let's add a bunch of `<game-card>` items to our app. We will be using a `v-for` loop just like we did before, but we're going to loop on top of our custom components this time!

```html
<div id="app">
  <awesome-button></awesome-button>
  <hr>
  <game-card v-for="game in games" :game-data="game" :key="game.name"></game-card>
</div>
```

That is quite a bit of `game` being tossed around, so let's look at it in detail.

First step, we are creating our `<game-card>` component, like we discussed earlier.

After, we add the `v-for="game in games"` loop like we saw last week. This creates a `game` variable that will hold the current game in the loop, and we can use it right away!

Finally, we assign to the template's **prop**, `gameData`, a value, in this case our `game` variable from the loop. Notice that instead of camel case, we're using a hyphen `game-data` because HTML is case insensitive. If you are having a hard time grasping this, try thinking it in object terms. We are doing something similar to `game-card.props.gameData = game`

Don't forget about the `:key`!

There is a huge gotcha to mention here, we are passing `game` to our `game-data` prop, but there is a `:` behind it. Did you notice?

When we assign a property to a component instance, there's two ways to go about it. Following our example, we could either do it with the `:` before it (this is a shorthand for `v-bind:`!). This will make sure the data that we are passing **after** the `="<here>"` is used by JavaScript as a variable, or an actual piece of **code**. 

If you were to type instead `gameData="game"`, then *Vue* will take this as assigning the `gameData` prop the **String** `"game"`. So something like: `game-card.props.gameData = "game"`!

Go ahead and take a break from theory and actually go and run this in your browser. You will see as expected, that our whole `<game-card>` component's template is being rendered for each one of our `game`'s. 

The greatest part about this is that if we were to make a change to our HTML, it will be updated everywhere in our app. 

Also, and **most importantly**, _components_ allow you to **contain** the logic for that particular component. Let's revisit that `game-card`'s `increaseRating()` method.

# Component Data vs. Props

Props for components actually can be a very lengthy subject, but there is a very important rule of thumb that you must always keep in mind. A property should NEVER be modified from inside a component. 

In fact, if you try to do this, *Vue* will throw all sorts of warnings and yell at you in the console - because this WILL lead to unexpected behavior. Here's the documentation, in case you want to read about it: [Mutating props in Vue2 is an anti-pattern](https://vuejs.org/v2/guide/migration.html#Prop-Mutation-deprecated).

How then, will we modify the value of `rating` inside of our component's storage? The key is in the question itself! We need to make a copy of this `prop` into our `data` so that we can actually modify it.

Let's first add our `data` to our `game-card` component, and assign it a new non-conflicting name (props and data props will conflict if named the same), and then pre-fill it with the prop's value.

```js
data() {
  return {
    game: {...this.gameData}
  }
},
```

Couple of things to note here, but before that, if you don't know yet what `{...gameData}` is doing, it's a spread operator. I won't go into full detail here and will try to post a brief article about it soon, but basically were making a *copy* of the `gameData` prop, because we don't ever want to modify that from the child.

The `data` property's `return`:

When we learned about the `data` property, I told you that it needed to hold an object with all the properties we needed, and this is how we've been doing it for our main **Vue instance**. However for **components** we actually need to make it a function, by adding `()`, and second `return` the actual object.

But WHY?! 😫 

Simply put, there can be one or many instances of your component, right? 

Each instance will need a *unique* data object! You don't want to share a single `data` object between all of those, or they would all share the same `title` for example - and the point of this whole app would be lost.

So the whole reason behind making it a **function** and *returning* an object, is that **Vue** can ☎️ call this function every time it *creates* one of our `game-cards`. That way each one will get a unique object of data to play with!

Accessing our props: 

When we create the `game`'s **data** property, we are assigning it `this.gameData`, so a couple of things to learn here. `props` can **also** be accessed within your component scripts via `this` just as your local state props from `data`. So here, we are setting `game` to be equal to the `gameData` property.

This means now we have to update our HTML, so inside the component switch the `gameData` reference for `game` like so:

```js
<div style="border-radius: .25rem; border: 1px solid #ECECEC;">
  <h2>{{ game.name }} - <small>{{ game.console }}</small></h2>

  <span v-for="heart in game.rating">❤️</span>

  <button @click="increaseRating">Increase Rating</button>
</div>
```

Run this once again in your browser, and you should get the same results.

Finally, we are at the point where we can make our `increaseRating` method work! Head to the method and replace the comment with the following:

```js
methods: {
  increaseRating() {
    this.game.rating++
  }
}
```

With every click, we're going to increment the component's **internal** `data` property which holds the `game`'s rating, -not- the prop.

---

There is a lot of theory to be learned about components, i've just began to scratch the surface, but hopefully you are starting to have a clearer picture of why frameworks like **Vue** are so popular, and so much fun to use.

From this point forward we're going to start looking at what I consider intermediate topics, like `computed` properties, `watchers`, `events`, etc. So hopefully you are excited to get to know the meaty part of Vue soon enough.

In case you need it, here's the complete code for today, and thanks for reading! 🤗😋

```html
<html>

<head>
  <title>Vue 101</title>
</head>

<body>
  <div id="app">
    <awesome-button></awesome-button>
    <game-card v-for="game in games" :game-data="game" :key="game.name"></game-card>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

  <script>
    Vue.component('awesome-button', {
      template: `<button @click="clickHandler">Click me for some awesomeness</button>`,
      methods: {
        clickHandler() {
          alert('YAS. 😎');
        }
      }
    });

    Vue.component('game-card', {
        props: ['gameData'],
        data() {
          return {
            game: {...this.gameData}
          }
        },
        template: `<div style="border-radius: .25rem; border: 1px solid #ECECEC; width: 400px; margin: 1rem; padding: 1rem;">
          <h2>{{ game.name }} - <small>{{ game.console }}</small></h2>

          <span v-for="heart in game.rating">❤️</span>

          <button @click="increaseRating">Increase Rating</button>
        </div>`,
        methods: {
          increaseRating() {
            this.game.rating++
          }
        }
      });

    const app = new Vue({
      el: '#app',
      data: {
        games: [
          { name: 'Super Mario 64', console: 'Nintendo 64', rating: 4 },
          { name: 'The Legend of Zelda Ocarina of Time', console: 'Nintendo 64', rating: 5 },
          { name: 'Secret of Mana', console: 'Super Nintendo', rating: 4 },
          { name: 'Fallout 76', console: 'Multiple', rating: 1 },
          { name: 'Super Metroid', console: 'Super Nintendo', rating: 6 }
        ]
      }
    });
  </script>
</body>

</html>
```