Vue JS Components

Introduction

A typical site usually has a header, sidebar, content and footer etc. When put together, they form the user interface. Vue JS allows us to define these individual sections as components that we can attach behaviour to. In a way, components in Vue JS allow us to extend HTML elements and encapsulate reusable code.

Consider a situation where you have to display a list of blog posts

With a Vue component, you can do something like the following

<posts-list></posts-list>

The rest can be handled by Vue (of course with some extra code)

The advantage of the above approach is that is it totally reusable and self-documenting. Instead of working with something generic like section in HTML, you can be more descriptive with names such as posts-list

By the time that you are done with this tutorial, you would have built the following components

VueJS Components

and re-factored the example below

Vue.JS Components

To be built using components as opposed to working with raw HTML the way we did in the previous example.

Topics to be covered

We will cover the following topics in this tutorial.

  • Vue JS Component Example
    • Template
    • Slots
    • Props
  • Vue JS Component Advanced Example
  • Tutorial Project (Status Updates) Re-factoring to use components

Vue JS Component Example

We will start with a very basic example. Our application will use Bulma CSS framework for styling and it will have the following files

  • index.html – this will be the main file that we will be interacting with.
  • app.js – this will

The skeleton structure of index.html will be as follows

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Vue.JS Directives</title>
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.3.1/css/bulma.min.css">
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css">
        <style type="text/css">
        body{padding: 40px}
        </style>
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
    </head>
    <body>
        <div class="container" id="app">

        </div>
        <script src="app.js"> </script>
    </body>
</html>

HERE,

  • We basically pull in vue.js, bulma and awesome css files from cdn networks. We also apply some padding to the body and create a container with id app which we will bind to our Vue instance.

Add the following code to the container div

<div class="columns">
    <div class="column is-one-quarter">
        <aside class="menu">
            <p class="menu-label">
                General
            </p>
            <ul class="menu-list">
                <li><a>Dashboard</a></li>
                <li><a>Customers</a></li>
            </ul>
            <p class="menu-label">
                Administration
            </p>
            <ul class="menu-list">
                <li><a class="is-active">Users</a></li>
            </ul>
            <p class="menu-label">
                Transactions
            </p>
            <ul class="menu-list">
                <li><a>Payments</a></li>
                <li><a>Transfers</a></li>
                <li><a>Balance</a></li>
            </ul>
        </aside>
    </div>
    <div class="column">
        <p class="menu-label">
            Content Section
        </p>
    </div>
</div>

HERE,

  • If you observe the menu generation pattern, it starts with a paragraph, followed by unordered list then repeats for every menu item.

Open index.html in the web browser

You should be able to see the following menu sections

Bulma CSS

Now, what we are going to do is use Vue component to generate the above menu.

Add the following code to app.js

Vue.component('app-menu',{
    template:`
    <div>
        <p class="menu-label">
            General
        </p>
        <ul class="menu-list">
            <li><a>Dashboard</a></li>
        </ul>
    </div>
    `
});

var app = new Vue({
    el: '#app',
    data: { }
});

HERE,

  • Vue.component('app-menu',{…}); defines a Vue component named app-menu.
  • template:…`` defines the component markup. In our case, it’s a markup used to create the menu. Note: we only included the markup for a single menu only. We also started with a div tag. This is because the returned result should be a single element while our actual menu component is made up of more than one element.
  • var app = new Vue({…}) instantiates an instance of the Vue object and binds it to the div container with the id of app

we can now modify index.html

replace all of the markup for the menu items and replace it with the following

<app-menu></app-menu>

Open index.html in your web browser.

You should be able to see only the dashboard menu

Let’s now replicate app-menu component three (3) times

<app-menu></app-menu>
<app-menu></app-menu>
<app-menu></app-menu>

You will get the following results

Bulma CSS

As you can see from the above example, we have reused the component to produce three (3) menu items. We can have as many as we want. This is great but everything looks exactly the same. We will need to be able to configure our component. Let’s now look at slots

Vue Slots

Let’s modify the app-menu component to the following

Vue.component('app-menu',{
    template:`
    <div>
        <p class="menu-label">
            General
        </p>
        <ul class="menu-list">
            <li><a><slot></slot></a></li>
        </ul>
    </div>
    `
});

HERE,

  • <slot></slot> is like a place holder in the list item. Its value is set to whatever value that is passed to the component

Modify the menu components as follows

<app-menu>Dashboard</app-menu>
<app-menu>Users</app-menu>
<app-menu>Payments</app-menu>

HERE,

  • <app-menu>Dashboard</app-menu> sets the slot value to Dashboard. The same applies to Users and Payments.

This example demonstrates how to use slots but we still haven’t achieved our objective. The menu heading still says GENERAL for all the menus.

Vue Component props

Props allow us to pass data to our component. For example, we can define a title prop for our menu component and configure when creating menu items. Let’s modify our component and include a property

Vue.component('app-menu',{
    props: ['title'],
    template:`
    <div>
        <p  class="menu-label">
            {{title}}
        </p>
        <ul class="menu-list">
            <li><a><slot></slot></a></li>
        </ul>
    </div>
    `
});

HERE,

  • props: ['title'], defines a title property for our component.
  • {{title}} displays the

Modify the menu components as follows

<app-menu title="General">Dashboard</app-menu>
<app-menu title="Administration">Users</app-menu>
<app-menu title="Transactions">Payments</app-menu>

HERE,

  • The attribute title is used to set the title of the menu. i.e. passing in General sets the title of the component to General while passing in Administration sets the title to administration.

Such is the power and flexibility of components. We have a reusable and configurable component that saves us duplicate code and is self-documenting. Remember, our original menu had a number of items under each main menu. The next section will address that.

Vue JS Component Advanced Example

This section combines a number of concepts and is a bit complex. We shall divide and conquer the code in its simplest form. We will first define the menu items in an array variable. This could be pulled from the database or defined in a variable like in this example.

var menus = [{
                title:'',
                sub_menus: [
                    {
                        name:'',
                        active:false,
                    },],
            },
            ];

HERE,

  • var menus = [{…}] defines an array variable that defines the menu headings and sub menu items
  • var app_menu_list_template = <div>...</div>; defines a template variable that contains the markup that we will use to build the menu. The template uses the component app-menu and uses the v-for directive to loop through the array. :title="menu.title" binds the value of menu.title to the prop title and :sub_menus="menu.sub_menus" binds the value of menu.sub_menus to the prop title.
  • var app_menu_template = <div>...</div>;> defines the menu item template variable that contains the markup that we will use to build the menu item. v-for="menu in sub_menus" loops through the sub menu array to fetch and display items.
  • Vue.component('app-menu-list',{…}); defines a vue component named app-menu-list. The component uses the template variable appmenulist_template for the template. It also defines a data array that returns the contents of the variable menu.
  • Vue.component('app-menu',{…}_; defines a vue component named app-menu. the component uses the template variable app_menu_template. Its also defines two props named title, and sub_menu. the template also defines a method isActive that is used to set the menu item to active if the active property of the menu is set to true.

The complete code for app.js is as follows

var menus = [{
                title:'General',
                sub_menus: [
                    {
                        name:'Dashboard',
                        active:true,
                    },
                    {
                        name:'Customers',
                        active:false,
                    },
                ],
            },
            {
                title:'Administration',
                sub_menus: [
                    {
                        name:'Users',
                        active:false,
                    }
                ],
            },
            {
                title:'Transactions',
                sub_menus: [
                    {
                        name:'Payments',
                        active:false,
                    },
                    {
                        name:'Transfers',
                        active:false,
                    },
                    {
                        name:'Balance',
                        active:false,
                    },
                ],
            },
        ];

var app_menu_list_template = 
`<div>
    <app-menu :title="menu.title" :sub_menus="menu.sub_menus" v-for="menu in menus">
    </app-menu>
</div>`;

var app_menu_template  = 
`<div>
    <p class="menu-label">
        {{title}}
    </p>
    <ul class="menu-list">
        <li v-for="menu in sub_menus"><a :class="isActive(menu)" >{{menu.name}}</a></li>
    </ul>
</div>`;

Vue.component('app-menu-list',{
    template: app_menu_list_template,
    data(){
        return {
            menus
        };
    }
});

Vue.component('app-menu',{
    props: ['title','sub_menus'],
    template:app_menu_template,
    methods:{
        isActive: function (menu) {
            return menu.active ? 'is-active' : '';
        }
    }
});

var app = new Vue({
    el: '#app',
    data: { }
});
The complete HTML for index.html is as follows
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Vue.JS Directives</title>
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.3.1/css/bulma.min.css">
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css">
        <style type="text/css">
        body{padding: 40px}
        </style>
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
    </head>
    <body>
        <div class="container" id="app">
            <div class="columns">
                <div class="column is-one-quarter">
                    <aside class="menu">
                    <app-menu-list></app-menu-list>
                    </aside>
                </div>
                <div class="column">
                    <p class="menu-label">
                        Content Section
                    </p>
                </div>
            </div>
        </div>
    <script src="app.js"> </script>
</body>
</html>

Open index.html in Google chrome

Press F12 to open developer tools and select Vue devtools

You should be able to see the following

VueJS Directives

Tutorial Project (Status Updates) Re-factoring to use components

In the previous tutorial, we build a simple status updates page using the v-for directive. We had all of the HTML in the index.html. In this section, we will re-factor the code and use components to build the status updates. Vue Devtools shows the nesting of our components as follows

VueJS Directives

As you can see from the above image, we have the UpdatesList component that nests the Update component which in turn nests StatusReplies component.

On the right hand side, we have the actual values for each component.

Create a new file main.js

Add the following code

var updates = [
            {
                name:'John Doe',
                handle:'johndoe',
                status:'Away on holiday',
                age:'31m',
                avatar:'http://lorempixel.com/64/64/people/',
                replies: [
                    {
                        name:'Mary Jane',
                        handle:'mary-j',
                        status:'Bring me a souvenir bun',
                        age:'10m',
                        avatar:'http://lorempixel.com/64/64/sports/'
                    },
                ],
            },
            {
                name:'Mary Jane',
                handle:'mary-j',
                status:'Rocking Vue.JS Tuts :)',
                age:'10s',
                avatar:'http://lorempixel.com/64/64/sports/',
                replies: [
                ],
            },
            {
                name:'Jack Smith',
                handle:'jsmith',
                status:'Cooking dinner for the family',
                age:'2 days',
                avatar:'http://lorempixel.com/64/64/food/'
            },
            {
                name:'Alan Shore',
                handle:'mrshore',
                status:'Oh yeah! black sails.',
                age:'13m',
                avatar:'http://lorempixel.com/64/64/nature/'
            },
        ];

var updates_list_template = 
`<div>
    <update :avatar="update.avatar" :replies="update.replies" v-for="update in updates">
        <template slot="name">{{update.name}}</template>
        <template slot="handle">{{update.handle}}</template>
        <template slot="status">{{update.status}}</template>
        <template slot="age">{{update.age}}</template>
    </update>
</div>`;

var update_template = 
`<div class="box">
    <article class="media">
        <div class="media-left">
            <figure class="image is-64x64">
                <img :src='avatar' alt="Image">
            </figure>
        </div>
        <div class="media-content">
            <div class="content">
                <p>
                    <strong><slot name="name"></strong> <small>@<slot name="handle"></small> <small><slot name="age"></small>
                    <br>
                    <slot name="status">
                </p>
            </div>
            <nav class="level">
                <div class="level-left">
                    <a class="level-item">
                        <span class="icon is-small"><i class="fa fa-reply"></i></span>
                    </a>
                    <a class="level-item">
                        <span class="icon is-small"><i class="fa fa-retweet"></i></span>
                    </a>
                    <a class="level-item">
                        <span class="icon is-small"><i class="fa fa-heart"></i></span>
                    </a>
                </div>
            </nav>
            <status-replies :replies="replies"></status-replies>
        </div>
    </article>
</div>`;

var status_reply_template = 
`<div>
<div class="box" v-for='reply in replies'>
    <article class="media">
        <div class="media-left">
            <figure class="image is-64x64">
                <img :src="reply.avatar" alt="Image">
            </figure>
        </div>
        <div class="media-content">
            <div class="content">
                <p>
                    <strong>{{reply.name}}</strong> <small>@{{reply.handle}}</small> <small>{{reply.age}}</small>
                    <br>
                    {{reply.status}}
                </p>
            </div>
        </div>
    </article>
</div>
</div>`;

Vue.component('updates-list',{
    template: updates_list_template,
    data(){
        return {
            updates
        };
    }
});

Vue.component('update',{
    props: ['avatar','replies'],
    template: update_template,
});

Vue.component('status-replies',{
    props: ['replies'],
    template: status_reply_template,
});

Vue.component('post-reply',{
    template:`
    <div class="modal is-active" id="modal-ter">
            <div class="modal-background"></div>
            <div class="modal-card">
                <header class="modal-card-head">
                    <p class="modal-card-title">Reply to  status</p>
                    <button @click="$emit('close')" class="delete"></button>
                </header>
                <section class="modal-card-body">
                    <p class="control">
                        <textarea class="textarea" placeholder="Textarea"></textarea>
                    </p>
                </section>
                <footer class="modal-card-foot">
                    <a class="button is-success">Post Reply</a>
                </footer>
            </div>
        </div>
    `
});

var app = new Vue({
    el: '#app',
    data: {
        message: 'Hello Vue!',
        value:'Welcome to the tutorial <small>which is all about Vue.js</small>',
        viewed:true,
        updates:updates,
        showReplyModal: false,
    }
});

Create a new file index.html and add the following code

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Vue.JS Directives</title>
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.3.1/css/bulma.min.css">
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css">
        <style type="text/css">
        body{padding: 40px}
        </style>
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
    </head>
    <body>
        <div class="container" id="app">
            <h2 class="title">Status Updates</h2>
            <updates-list></updates-list>
        </div>
        <script src="main.js"> </script>
    </body>
</html>

You should be able to see the following

Vue.JS Components

Summary

In this tutorial, we have looked at what components are and how to use them. We also looked at how you can use components in a real world application.

What’s next?

The next tutorial will be on events in Vue.js.

Kode Blog Tutorials is dedicated to bring you update to date, high quality free tutorials. You can support us by using the social media buttons to like and share the tutorial and subscribing to our newsletter. Please use the comments section below to give us feedback.

You can sign up for a free account on our site and you will be able to keep track of your progress as you go through the tutorial series and get notifications when we publish new content.

Related Tutorials