The way components communicate, in Vue, is through props and custom events. And with Vue 2.0 release, this system has changed quite a bit — it has actually gotten simpler!
But still, this transition might not feel so obvious to many developers. So, in this article, I’d like to show you the different cases in which components communicate, and how to handle each one properly.
Let’s start first by defining the three cases that components communicate with one another:
props
! Nothing more, nothing less!I’ve already explained how the first case works. So, let’s get into the other two cases.
It’s called parent-child communication because they are directly related to each other — note that we’re talking here about the second case, which is from child to its direct parent.
To accomplish this kind of communication, you have to do two things:
$emit(eventName, [data])
.v-on:event-name
directly in the template where the child component is used.Here’s a quick example:
Let’s say you’ve built your own custom dropdown component that contains a bunch of options which you pass from the parent. When an option is selected in that dropdown, it should emit an event to its parent, so it can do its stuff.
Here’s what happens when an option is selected in Dropdown.vue
:
methods: {
selected () {
// assume 'this.selectedOption' is defined in the data object
this.$emit('option-select', this.selectedOption)
}
}
Now in the parent component — let’s call it NavBar.vue
— we’ll have something like this (note that I’m using the shorthand for v-on
which is @
):
<template>
<div class="nav-bar">
<!-- other elements -->
<dropdown
:options="someOptions"
@option-select="onOptionSelect"
></dropdown>
</div>
</template>
<script>
export default {
methods: {
onOptionSelect (option) {
// handle it here
}
}
}
</script>
To make it easy to differentiate between listening to custom events and native events (click
, keyUp
, etc), Vue 2.0 lets us specify that using the .native
modifier. Here’s an example:
<my-component @click.native="onComponentClick"></my-component>
This means, when <my-component>
is clicked, we’ll treat it as any other normal event on any element (such as <button>
).
This also means, you can define your own custom click
event on that component without any conflicts — maybe you want that event to be emitted when the user clicks on a particular part in that component — not the whole component.
In this case you don’t have to use that modifier:
<!— listening to click event which was emitted from my-component —>
<my-component @click="onComponentClick"></my-component>
In other words, global component communication. In Vue 1.0, we were handling these kind of events using the two instance methods:
$dispatch
: to fire an event that propagates upward along the parent chain.$broadcast
: to broadcast an event that propagates downward to all descendants (all children).Now in Vue 2.0, those methods are deprecated; there are several reasons for that:
this.$root.broadcast('event-name')
, which doesn’t feel good, does it?So, what’s the solution instead? Well, the solution is get rid of those methods altogether, and handle all the communications through what we call the event hub (or some call it event bus).
In its simplest definition, it’s an empty Vue instance that you use globally to fire and listen for events anywhere you want in the component’s tree.
So, obviously, like any other vue instance, we have these methods available for that:
eventHub.$emit(eventName)
to emit an event.eventHub.$on(eventName)
to listen for an event.eventHub.$off(eventName)
to remove event listeners.As I just mentioned, this eventHub
is an empty Vue instance which you’d create like this: const eventHub = new Vue()
.
For everything to work correctly, you need to make sure that this eventHub
is accessible globally. And this depends on how your application is structured.
If you’re building a simple non-modular app, you can attach that instance to the browser’s window
object.
However, for module-based applications that use bundling tools like Webpack
or Browserify
, you can do that by exporting that instance from a certain file and then import it wherever you need it.
// src/shared/EventHub.js
export default new Vue()
Now in other components, you would import it like this:
import eventHub from 'src/shared/EventHub'
Let’s say, you have this component’s tree:
component-one
=> component-a, component-b
component-two
=> component-c, component-d
In this example, we have two main components named component-one
and component-two
where each one contains other nested components, as described above.
Let’s imagine that we want to pass an event from component-b
to component-c
.
To do that, we first need to emit that event from component-b
:
import eventHub from 'src/shared/EventHub'
// …
methods: {
doSomething () {
eventHub.$emit('component-b-did-something', someData)
}
}
Now, in component-c
, you would listen to it like this:
import eventHub from 'src/shared/EventHub'
// …
mounted () {
eventHub.$on('component-b-did-something', (someData) => {
// handle it however you want.
})
}
As simple as that! The great thing about this approach is that you’re no longer concerned about the components tree structure. In other words, events can be emitted from any component and handled by any other component regardless of their relationship.
Although it seems that this is the ultimate solution for any complex components communication, it is not! Because imagine how unmaintainable this can become as your app grows more in complexity.
Luckily, for those cases we have a dedicated solution for that, which is Vuex. If you’re building any complex SPA and you’re not using Vuex yet, I highly encourage you to take the time to learn about it as it’ll make your life much easier in those case.
To finish up this post, let’s review the different cases of components communication:
props
.$emit(eventName)
in children and the directive v-on:event-name
in the parent.I'm a freelance web developer. Laravel & VueJS are my main tools these days and I love building stuff using them. I write constantly on this blog to share my knowledge and thoughts on things related to web development... Let's be friends on twitter.