VueJS Components : Nested Components, Props and Directives

Biswarup Banerjee  Print   17 min read  
15 Dec 2018
15 May 2019
Intermediate
1.25K

Whenever we are developing a VueJS application we tend to break down the entire application into smaller parts, each part having a specific role.

Each of these smaller parts is called components and these very components come together like building blocks and give the application a complete form.

Let’s take the example of a school building. What can we expect to have in a school building?

Room
Role
Classroom
For having classes
Library
For reading and issuing books
Sports Zone
For playing and practising sports
Washroom
For biological reasons
Staff room
For teachers and other staffs

Now let’s see what all features and functionalities we can have in let’s say a social media webapp built with VueJS?

Component
Role
Timeline
To show posts of friends
Chat Box
To chat with friends
Search Box
To search friends
Profile
To post about self-profile

Now I am pretty sure the readers can relate the components in a VueJS web app with rooms in a building.

To make it more clear, I would like to add, just like the rooms in a building are allocated a specific role and each of the rooms are separate and independent, similarly, components are individual and independent and each of the components have a very specific task to accomplish.

And just like each of the rooms: the classroom, the library and other rooms come together to give the form to the school building as a whole, similarly, the timeline component, the search friends component and other components come together to give the social media web app a whole form.

Components:

Let’s understand the component structure by a hands on approach: that is by building a small app with VueJS.

So what we will be making is a simple portfolio page but by using the component structure.

Rough idea as of what we want to build:

In the previous article Getting-Started-With-Vue it was discussed how to install the vue-cli.

So in this article let’s start by creating an app with the vue-cli.

> vue create my-portfolio

This above command will direct the vue-cli to create an app template by the name my-portfolio.

Once the app is created to run the second command :

> vue run serve

This command will run the vue app in port 8080 (default)

Now we have our my-portfolio app running locally in localhost:8080.

There will be many folders created by the vue-cli by default but since we are at the very beginning of the course we will focus only on two main parts:

  • The components folder
  • The App.vue file

In the components folder we will be creating all of our components and in the App.vue we will be integrating aka importing all of our components and will be giving our app a form.

Initially, the localhost:8080 will look something like this:

Step1: Remove the HelloWorld component from App.vue page

To get started with developing our web app we should first remove the default imported components.

After the component is removed a blank white error free page should get loaded in the localhost:8080

Step2: Create component Navbar in components folder

So to create navbar component, create Navbar.vue file under the components folder.

// Navbar.vue
<template>
 <div>
 <div class="topnav">
 <a class="active" href="#home">Home</a>
 <a href="#news">News</a>
 <a href="#contact">Contact</a>
 <a href="#about">About</a>
 </div>
 </div>
</template>

<script>
export default {
 name: 'Navbar',
 props: {
 }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.topnav {
 overflow: hidden;
 background-color: #333;
}

.topnav a {
 float: left;
 color: #f2f2f2;
 text-align: center;
 padding: 14px 16px;
 text-decoration: none;
 font-size: 17px;
}

.topnav a:hover {
 background-color: #ddd;
 color: black;
}

.topnav a.active {
 background-color: #4CAF50;
 color: white;
}
</style>

After the component is created it is to be imported in the App.vue file.

After importing the App.vue file will look like:

// App.vue
<template>
 <div id="app">
 <Navbar></Navbar>
 </div>
</template>

<script>
import Navbar from './components/Navbar.vue'

export default {
 name: 'app',
 components: {
 Navbar
 }
}
</script>

<style>
body {
 margin: 0;
 font-family: Arial, Helvetica, sans-serif;
}
</style>

After the import is complete you can see the Navbar in the localhost:8080.

Step3: Create other components just the way we created the Navbar component

So feel free to use your own HTML/CSS skills to develop the other 3 components:

  1. The Profile Details component

  2. The Image component

  3. The Footer component

  1. The profile details component: (Profile.vue)

    // Profile.vue 
    <template>
     <div>
     <div class="card">
     <img src="http://allworldpm.com/wp-content/uploads/2016/10/230x230-avatar-dummy-profile-pic.jpg" alt="John" style="width:100%">
     <h1>John Doe</h1>
     <p class="title">Software Engineer, Google CA</p>
     <p>Harvard University</p>
     <div style="margin: 24px 0;">
     <a href="#"><i class="fa fa-dribbble"></i></a>
     <a href="#"><i class="fa fa-twitter"></i></a> 
     <a href="#"><i class="fa fa-linkedin"></i></a> 
     <a href="#"><i class="fa fa-facebook"></i></a>
     </div>
     <p><button>Contact</button></p>
     </div>
     </div>
    </template>
    
    <script>
    export default {
     name: 'Profile',
     props: {
     }
    }
    </script>
    
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>
    .card {
     box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
     max-width: 300px;
     margin: auto;
     text-align: center;
     font-family: arial;
    }
    
    .title {
     color: grey;
     font-size: 18px;
    }
    
    button {
     border: none;
     outline: 0;
     display: inline-block;
     padding: 8px;
     color: white;
     background-color: #000;
     text-align: center;
     cursor: pointer;
     width: 100%;
     font-size: 18px;
    }
    
    a {
     text-decoration: none;
     font-size: 22px;
     color: black;
    }
    
    button:hover, a:hover {
     opacity: 0.7;
    }
    </style>
    

  2. The Image component : (Image.vue)

    // Image.vue
    <template>
     <div>
     <img src="https://www.hassanlab.eu/sites/default/files/images/Paris.png" alt="Paris" width="300" height="300">
     </div>
    </template>
    
    <script>
    export default {
     name: 'Image',
     props: {
     }
    }
    </script>
    
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>
    img {
     border-radius: 8px;
     margin: 20px;
     margin-bottom: 100px;
     float: left;
    }
    </style>
    
  3. The Footer component: (Footer,vue)

    Footer.vue
    <template>
     <div>
     <div class="footer">
     <p>This is a demo footer. (c) 2018-19</p>
     </div>
     </div>
    </template>
    
    <script>
    export default {
     name: 'Footer',
     props: {
     }
    }
    </script>
    
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>
    .footer {
     position: fixed;
     left: 0;
     bottom: 0;
     width: 100%;
     background-color: #333;
     color: white;
     text-align: center;
    }
    </style>
    

Step4: Integrate aka import all the components in App.vue

After importing all the components under App.vue the App.vue file will look like:

// App.vue
<template>
 <div id="app">
 <Navbar></Navbar>
 <div class="container">
 <Profile></Profile>
 <ImageComponent></ImageComponent>
 <ImageComponent></ImageComponent>
 <ImageComponent></ImageComponent>
 <ImageComponent></ImageComponent>

 </div>
 <Footer></Footer>
 </div>
</template>

<script>
import Navbar from './components/Navbar.vue'
import Footer from './components/Footer.vue'
import Profile from './components/Profile.vue'
import ImageComponent from './components/Image.vue'


export default {
 name: 'app',
 components: {
 Navbar,
 Footer,
 Profile,
 ImageComponent
 }
}
</script>

<style>
body {
 margin: 0;
 font-family: Arial, Helvetica, sans-serif;
}
.container {
 padding: 40px;
}
</style>

Once we are done creating and importing all the components, the localhost:8080 will look somewhat like:

PS: If my readers are wondering how to show different images in the same Image component and not show the same image in all the Image Components, then for that we need to use props to pass the image URL to each of the components. We will be discussing ‘props’ here as well.

Why do we need components?

Any reader who is new to VueJS or similar javascript frameworks like ReactJS or Angular might be confused as of why do we need to implement components in the first place?

Can’t we simply start building the app directly from the top of the page to the bottom of the page? Isn’t dividing a page into components and then again putting those components together with a more hectic task?

So let me start by throwing a problem.

Suppose you have a 20 paged website and you need a chat functionality (similar to the chat box component mentioned earlier) in 10 pages of those 20 paged websites. So if your chat functionality is not in a different independent component then you have to copy paste the codes related to your chat component in all the 10 pages again and again.

That will unnecessarily take up a huge amount of your time and will make the codebase messy and unclear.

Suppose you need the search feature in 6 of your pages and the profile feature in another 8 of your pages. Now just imagine the complexity that will arise if you keep on copy-pasting the codes to create features in pages.

So here comes the concept of components to rescue. How?

Now suppose you had the features separated as components. Then you simply had to import the components in each of the pages as and when required. Importing requires very minimal lines of extra coding and complexity and gives you zero code redundancy.

With components: write once, use everywhere.

To jot down everything, the main reasons we need components are:

  1. Reusability: Components allow the developer to reuse the once made component again and again throughout the entire application without increasing the code complexity and keeping the codebase non-redundant.

  2. Maintainable and organized code: The component structure of a VueJS app allows the code to be cleaner and more maintainable as it reduces code redundancy. So if there is some bug in the search feature then the developer need not browse through all of the codebases but can simply work on the Search Component to find the bug.

  3. Testing: When it comes to testing the web app, having a component structure helps a lot and every component can be unit tested.

What are Props?

So in the previous example, we saw that we used the Image Component 4 times. But all the 4 had the same image.

So how to customize each component?

Here come ‘props’ to rescue. With props, you can send values to each component and customize them.

Through props, you can pass a number, a boolean, a string, an array, an object or properties of an object. These can be static or even dynamic. You can pass either one prop or multiple props to a single component.

Now let’s see an implementation of props:

So we modified the Image component and after some modifications our Image.vue looks like:

// Image.vue
<template>
 <div>
 <img :src=imageUrl width="300" height="300">
 </div>
</template>

<script>
export default {
 name: 'Image',
 props: {
 imageUrl: String,
 }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
img {
 border-radius: 8px;
 margin: 20px;
 margin-bottom: 100px;
 float: left;
}
</style>

So what we changed is basically instead of giving a static URL in the img tag we replaced it with a dynamic value that we are receiving from the ‘props’.

For the same, we had to modify our App.vue template as well:

// App.vue
<template>
 <div id="app">
 <Navbar></Navbar>
 <div class="container">
 <Profile></Profile>
 <ImageComponent
 imageUrl="https://cdn-image.travelandleisure.com/sites/default/files/styles/1600x1000/public/1446842493/7-paris-social-niche1115.jpg?itok=H1i3qUcr">
 </ImageComponent>
 
 <ImageComponent
 imageUrl="https://www.airambulancecard.com/wp-content/uploads/2016/02/paris.jpg">
 </ImageComponent>
 <ImageComponent
 imageUrl="http://www.maierandmaierphotography.com/wp-content/uploads/2013/05/sacre-coeur-paris-france.jpg">
 </ImageComponent>
 <ImageComponent
 imageUrl="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQT4XQeqmbZGnq3XsbxICuGraLyPZzzHu3Z9jER5mADzzsO3I47">
 </ImageComponent>
 </div>
 <Footer></Footer>
 </div>
</template>

So now from App.vue we are sending a different URL to each of the components as props so that each of the components is customized.

And this would result in our web page having 4 different images and it would look something like:

So after all these coding we have a web page built in VueJS and is divided into components that are customized by props!

Components vs Directives

So far we have discussed in details what are components, why and how should they be used in VueJS while building apps.

In this part, we would go through what are directives and how are directives different from components.

So directives basically integrate certain functionalities or behaviours to the components we have created or to already existing DOM elements.

A directive does not have its own view, unlike a component which has its own view.

So it comes down to the fact that, when we create a component a view is created along with its associated behaviours.

But when a directive is defined, it just adds behaviours to components or to already existing DOM elements.

Let us now add some directives to our portfolio page that we have been building:

So we add the ‘v-on:click’ directive.

// Image.vue
<template>
 <div>
 <img :src=imageUrl v-on:click="showAlert" width="300" height="300">
 </div>
</template>

What happens here is basically when you click on on the Image Component from now on because of the v-on:click directive the method ‘showAlert’ will be called.

And we have defined the method in the following way inside the script tag:

// Image.vue
<script>
export default {
 name: 'Image',
 props: {
 imageUrl: String,
 },
 methods: {
 showAlert() {
 alert('Image is clicked');
 }
 }
}
</script>

From now on in our app whenever any of the images are clicked an alert is displayed.

So the Image component is responsible for the view and the v-on:click is responsible for the associated behaviour.

Nested Components

So what if now we want an ImageTag component inside every Image Component?

There comes the concept of Nested components.

In simpler words importing components inside components is called nesting of components.

Let’s get started with the coding of nested components in our already existing project.

Step1: Create the ImageTag component

Under the components, folder creates the file ImageTag.vue :

// ImageTag.vue
<template>
 <div>
 <p>This is image tag</p>
 </div>
</template>

<script>
export default {
 name: 'ImageTag',
 props: {
 }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
p {
 text-align: center;
 font-family: 'Courier New', Courier, monospace;
 border: 1px solid burlywood;
 margin: 5px;
 padding: 5px;
}
</style>

The above code block will create the component ImageTag.

Step2: Import ImageTag component inside Image Component

Now simply import the ImageTag component inside the already existing Image Component.

// Image.vue
<template>
 <div class="image-container">
 <img :src=imageUrl v-on:click="showAlert" width="300" height="300">
 <ImageTag></ImageTag>
 </div>
</template>

<script>
import ImageTag from '../components/ImageTag.vue'

export default {
 name: 'Image',
 props: {
 imageUrl: String,
 },
 components: {
 ImageTag,
 },
 methods: {
 showAlert() {
 alert('Image is clicked');
 }
 }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
img {
 border-radius: 8px;
 margin: 10px;
 /* margin-bottom: 100px; */
}
.image-container {
 float: left;
 margin-bottom: 100px;
}
</style> 

That’s all.

Now you have the ImageTag component nested under the Image Component.

And your page will now look somewhat like:

Now, in this case, the Image component is the parent component and the ImageTag is the child component.

The parent component and the child component can pass props and interact with each other.

Summary

I hope my readers have gained a deeper understanding of VueJS components after going through the article. So we build a VueJS app, that had a component structure and we passed props through it. Then we also explored the basics of VueJS directives and also created nested components.

My readers can try implementing props on nested components and customize each of the Image Tags as a takeaway assignment.

Take our free skill tests to evaluate your skill!

In less than 5 minutes, with our skill test, you can identify your knowledge gaps and strengths.

Learn to Crack Your Technical Interview

+
+
Accept cookies and close this message