Introduction to working with components in Vue.js

One of the great things about working with Vue is its component-based approach to building user interfaces. This allows you to break your application into smaller, reusable pieces (components) which you can then use to build out a more complicated structure.

I’ll look at how to create components, how to pass data between components (via both props and an event bus) and how to use Vue’s `` element to render additional content within a component.Each example will be accompanied by a runnable CodePen demo.

How to Create Components in Vue

Components are essentially reusable Vue instances with a name. There are various ways to create components within a Vue application. For example, in a small- to medium-sized project you can use the Vue.component method to register a global component, like so:

Vue.component('my-counter', {
  data() {
    return {
      count: 0
    }
  },
  template: `{{ count }}
`
})

new Vue({ el: '#app' })

The name of the component is my-counter. It can be used like so:


  

When naming your component, you can choose kebab case (my-custom-component) or Pascal case (MyCustomComponent). You can use either variation when referencing your component from within a template, but when referencing it directly in the DOM (as in the example above), only the kebab case tag name is valid.

You might also notice that, in the example above, data is a function which returns an object literal (as opposed to being an object literal itself). This is so that each instance of the component receives its own data object and doesn’t have to share one global instance with all other instances.

There are several ways to define a component template. Above we are using a template literal, but we could also use a `


As you can see, when using single-file components, it’s possible to import and use these directly within the components where they’re needed.

In this guide, I’ll present all of the examples using the `Vue.component()` method of registering a component.

Using single-file components generally involves a build step (for example, with [Vue CLI](https://morioh.com/p/aa4132026039)).

## Passing Data to Components Via Props

Props enable us to pass data from a parent component to child component. This makes it possible for our components to be in smaller chunks to handle specific functionalities. For example, if we have a blog component we might want to display information such as the author’s details, post details (title, body and images) and comments.

We can break these into child components, so that each component handles specific data, making the component tree look like this:


If you’re still not convinced about the benefits of using components, take a moment to realize how useful this kind of composition can be. If you were to revisit this code in the future, it would be immediately obvious how the page is structured and where (that is, in which component) you should look for which functionality. This declarative way of composing an interface also makes it much easier for someone who isn’t familiar with a codebase to dive in and become productive quickly.

Since all the data will be passed from the parent component, it can look like this:

new Vue({
el: ‘#app’,
data() {
return {
author: {
name: ‘John Doe’,
email: ‘jdoe@example.com’
}
}
}
})


In the above component, we have the author details and post information defined. Next, we have to create the child component. Let’s call the child component `author-detail`. So our HTML template will look like this:


We’re passing the child component the `author` object as props with the name `owner`. It’s important to note the difference here. In the child component, `owner` is the name of the prop with which we receive the data from the parent component. The data we want to receive is called `author`, which we’ve defined in our parent component.

To have access to this data, we need to declare the props in the `author-detail` component:

Vue.component(‘author-detail’, {
template: `

  ## {{ owner.name }}

{{ owner.email }}

´,
props: [‘owner’]
})


We can also enable validation when passing props, to make sure the right data is being passed. This is similar to [PropTypes](https://reactjs.org/docs/typechecking-with-proptypes.html) in React. To enable validation in the above example, change our component to look like this:

Vue.component(‘author-detail’, {
template: `

  ## {{ owner.name }}

{{ owner.email }}

`,
props: {
owner: {
type: Object,
required: true
}
}
})


If we pass the wrong prop type, you’ll see an error in your console that looks like what I have below:

“[Vue warn]: Invalid prop: type check failed for prop ‘text’. Expected Boolean, got String.
(found in component )”


There’s an [official guide in the Vue docs](https://vuejs.org/v2/guide/components-props.html#Prop-Validation) that you can use to learn about prop validation.

[**codepen.io**](https://codepen.io/SitePoint/pen/NegVgK)

## Communicating From a Child to Parent Component via an Event Bus

[Events](https://vuejs.org/v2/guide/events.html) are handled by creating wrapper methods that are triggered when the chosen event takes place. By way of a refresher, let’s build on our original counter example, so that it increases each time a button is clicked.

This is what our component should look like:

new Vue({
el: ‘#app’,
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
}
}
})


And our template:

{{ count }}

+

This is hopefully simple enough. As you can see, we’re hooking into the `onClick` event to trigger a custom `increase` method whenever the button is clicked. The `increase` method is then incrementing our `count` data property. Now let’s expand the example to move the counter button into a separate component and display the count in the parent. We can do this using an event bus.

Event buses come in handy when you want to communicate from a child component to parent component. This is contrary to the default method of communication, which happens from parent to child. You can make use of an event bus if your application isn’t big enough to require the use of [_Vuex_](https://morioh.com/p/3fab259c3d25).

So here’s what we want to do: the `count` will be declared in the parent component and passed down to a child component. Then in the child component, we want to increment the value of count and also ensure that the value is updated in the parent component.

The App component will look like this:

new Vue({
el: ‘#app’,
data() {
return {
count: 0
}
}


Then in the child component, we want to receive the count via props and have a method to increment it. We don’t want to display the value of count in the child component. We only want to do the increment from the child component and have it reflected in the parent component:

Vue.component(‘counter’, {
template: `

  +

`,
props: {
value: {
type: Number,
required: true
}
},
methods: {
increment() {
this.count++
}
}
})


Then our template will look like this:

{{ count }}

If you try incrementing the value like that, it won’t work. To make it work, we have to emit an event from the child component, send the new value of `count` and also listen for this event in the parent component.

First, we create a new instance of Vue and set it to `eventBus`:


const eventBus = new Vue();



We can now make use of the event bus in our component. The child component will look like this:

Vue.component(‘counter’, {
props: {
count: {
type: Number,
required: true
}
},
methods: {
increment() {
this.count++
eventBus.$emit(‘count-incremented’, this.count)
}
},
template: `

+

`
})


The event is emitted each time the `increment` method is called. We have to listen for the event in the main component and then set `count` to the value we obtained through the event that was emitted:

new Vue({
el: ‘#app’,
data() {
return {
count: 0
}
},
created() {
eventBus.$on(‘count-incremented’, (count) => {
this.count = count
})
}
})


Note that we’re making use of [Vue’s created lifecycle method](https://vuejs.org/v2/api/#created) to hook into the component before it’s mounted and to set up the event bus.

Using an event bus is good if your application isn’t complex, but please remember that, as your application grows, you may need to make use of Vuex instead.

[**codepen.io**](https://codepen.io/SitePoint/pen/qLjzZL)

## Nesting Content in Components Using Slots

In all the examples we’ve seen so far, the components have been self-closing elements. However, in order to make components that can be composed together in useful ways, we need to be able to nest them inside one another as we do with HTML elements.

If you try using a component with a closing tag and putting some content inside, you’ll see that Vue just swallows this up. Anything within the component’s opening and closing tags is replaced with the rendered output from the component itself:

This will be replaced


Luckily, [Vue’s slots](https://vuejs.org/v2/guide/components-slots.html) make it possible to pass an arbitrary value to a component. This can be anything from DOM elements from a parent component to a child component. Let’s see how they work.

The script part of our components will look like this:

Vue.component(‘list’, {
template: ‘#list’
})

new Vue({
el: “#app”
})


Then the templates will look like this:

Slots

#### I am the first slot



#### I am the second slot

The content inside our `` component gets rendered between the `` element tag. We can also make use of fallback content, for cases where the parent doesn’t inject any.

Slots

#### I am the first slot

The fallback content will render in cases where there’s no content from the parent component: [**codepen**](https://codepen.io/SitePoint/pen/zyzQgR)

## Conclusion

This has been a high-level introduction to working with components in Vue. We looked at how to create components in Vue, how to communicate from a parent to a child component via props and from a child to a parent via an event bus. We then finished off by looking at slots, a handy method for composing components in useful ways. I hope you’ve found the tutorial useful.

#vue-js #javascript #web-development

Introduction to working with components in Vue.js
27.85 GEEK