Vue.js Advanced: Mastering Events
Events are Vue’s way of handling communication from children to parent components. But although the basics are very simple, the way you leverage on events will define a big deal of your application architecture.
Declarative vs Imperative
Children don’t give orders to parents.
Or in more technical words: events should be declarative, not imperative.
The rule above leads to a more flexible architecture, since we are just reporting stuff that happened.
This way it’s easy for us to reuse the component within another context, which can (and will) do completely different things when the events we first created are emitted.
❌ Consider this bad example:
1 2 3 4 5
<template> <button @click="$emit('create-post')"> Add </button> </template>
create-post is giving an order. It’s being imperative, not declarative.
For example, in another context where we don’t want to
create-post but just add it to a list so we can save in batches later, this event’s name would lose it’s meaning.
✅ But if we just did something like this:
1 2 3 4 5
<template> <button @click="$emit('add-click')"> Add </button> </template>
Then we are reporting what happened. The user clicked the “Add” button, and nothing more than that.
What happens from here on is a responsibility of the parent component, and the button doesn’t need to concern itself with this information.
Following the suggestions from the last topic about being declarative, a trick I tend to use to be sure my event naming is coming out right, is to try to compose it in a way that the listening directive sounds like plain english.
We could read it like:
on “add post” click, save the post
Sounds nicely, right? :)
How to send an inverted event (from parent to children)?
Spoiler: you don’t!
Sometimes you can feel the urge to send an event from a parent component into a child, like when you want to change something within the child component but don’t have a prop controlling it.
If you feel this need, it’s Vue telling you that something in your components architecture is wrong.
Remember: when you use Vue, your UI is centered around data. Yes it does have events, but this doesn’t make it event-driven, Vue is data-driven.
If there is something that a father should control in a children, it should do it through data, and data only.
Global Event Bus
The event system within Vue is so useful that many people started using a new Vue instance just to use it as an event client. Thus the Global Event Bus pattern started popping up in the Vue.js community.
Basically you would do something like this:
1 2 3 4 5 6 7
const EventBus = new Vue // Anywhere you want to listen for the event EventBus.$on('my-event', someCallbackFunction) // Anywhere you want to trigger an event EventBus.$emit('my-event')
You should take care when using this pattern because it can make your application A LOT harder to reason about when used in the wrong spots or without restraint. But there are use cases that it can serve you well and solve your problem in a clean way.
I’m not gonna get too much into the Global Event Bus subject, but here is a link that explains very well it’s use cases and caveats.
Vue events are a rich topic of discussion, and being one of the two ways your components can communicate with the rest of the application makes it a very important topic as well.
Since I couldn’t find any advanced topic on the matter I decided to share what I learned by making mistakes, so you don’t have to. Any thoughts are welcome :)