Codementor Events

8 secrets Vue Developers must know

Published Feb 08, 2020

In an inline statement handler besides the $event special variable we have access to another special variable: arguments. $event will have access only to the first argument, whilst the arguments will have access to all arguments.

Detailed Explanation:

In an inline statement handler we already know that we have access to the special $event variable.

So if a child components emits an event with a parameter, we have access to it by using $event:

Parent component

<template> // ... <Child @event="someVariable = $event" /> // ...
</template>

Child component

export default { //... emitEvent () { this.$emit('event', 'a simple text') } //...
}

That works really well when the child component, is a component that we have access to, since we can be sure we pass only one parameter.

But how if we use a third-party component/library (e.g. dropzone.js) which passes many arguments up via event ??

The $event will have access only to the first argument. See this codesandbox example which illustrates that $event will catch only the first argument. (Click the button and see the console)

In that case, instead of $event we can use arguments and all arguments will be accessible.

So in the codesandbox above, in order to make it work, we have to change the line 4 in the Child.vue

from:

<GrandChild @event="$emit('event', $event)"/>

to:

<GrandChild @event="$emit('event', arguments)"/>

You can emit event from functional components

Short Explanation:

Using functional components, means that we don't have access to this context. Thus, we cannot do this.$emit(). But... we have access to listeners so we can do <button @click="listeners.clicked"></button>

Detailed Explanation:

Functional components are stateless (no reactive data) and instanceless (no this context). But functional components have access to some properties such as props, children etc and most importantly (for this case), listeners.

According to Vue docs:
listeners: An object containing parent-registered event listeners. This is an alias to data.on

That means that we can emit event from functional components. Wiii 😛

Simple example:

<template functional> <button @click="listeners['custom-event']('message from child')"> Button from child </button>
</template>

Working example (click the button and open the console)

How if we want to emit an event from functional component with render functions? Can we do that? Of course!

Simple example:

export default { functional: true, render(createElement, { listeners }) { return createElement( "button", { on: { click: event => { const emit_event = listeners.event_from_child; emit_event("Hello World!Is this the message we excpected? :/"); } } }, "Pass event to parent" ); }
};

Working example

Someone may wonder if we can use .sync Modifier using this approach.
The answer ? Of course!

<button @click="listeners['update:message']('some text')">Click me</button>

Pass all props to the child component

Say that we have a component which receives props and we want to pass all those props to a child component. To achieve that, we can do:

<ChildComponent v-bind="$props" />

Taking advantage of v-bind we can also have an object like:

data: () =>({ obj: { firstName: 'John', lastName: 'Doe', age: 30 }
})

And pass firstName, lastName, age as props to a child component, like:

Watch child properties changes from parent

You may wonder why to do that and you are right! That's a bad practice. But sometimes you use a third-party component and you want to watch their properties to fit your needs.

A long time ago, in a project we were using a date picker and we wanted to be able to detect when the popup was visible. Unfortunately there was no option to do that.I discovered that the date picker was using a popupVisible reactive property but it wasn't exposed by the library. So I had to somehow watch this property from my component.

The below code is NOT able to detect changes:

watch: { '$refs.datePicker.popupVisible': { handler (new_value) { console.log('value changed') }, deep: true } }

In order to detect changes of a child component's property you should do:

mounted() { this.$watch( "$refs.picker.popupVisible", (new_value, old_value) => { //execute your code here } ); }

Working example:

Learn more about vm.$watch

Listening for child event in the router-view

Most of you, should already know that since it is straightforward, but I've asked many times about the below question.

Say you have a component which has nested routes:

<template> //... <router-view></router-view> //...
</template>

And you have a nested route as follow:

<template> //... <button @click="$emit('event')"></button> //...
</template>

So the component corresponding to a nested route, emits an event. The question is : how to listen to that event ?

The simple answer demonstrated with code:

<template> //... <router-view @event="callAMethod"></router-view> //...
</template>

Exactly! We listen to that event in the router-view component

Vue components lifecycle hooks don't run in the order you think they run

Say you have 2 pages. Home and About.

When switching from Home to About, the created hook of the About component will run before the beforeDestroy hook of Home component. (take a moment here)

Weird?? Try switching routes in the working example below by seeing the console.

As solution (idk if it's the best solution though) you can use transition-mode out-in

<transition mode="out-in"> <router-view></router-view> </transition>

How to know if a child component is mounted ?

This one of my favorite tips I've read here (Vue Dose)

Say you have a child component and you want to do something when a hook of the child component is executed. You can do:

<Child @hook:created="doSomething" />

So the question to How to know when a child component mounted is:

<Child @hook:mounted="componentMountedDoSomething" />

How to know if a dynamically vuex registered module is registered ?

With the power of dynamically register/unregister vuex modules we can improve the performance a lot.

I recommend you to read a very useful article: Performance optimization: Lazy load vuex modules

We can register a vuex module:

this.$store.registerModule('my-module', MyModule)

And unregister it:

this.$store.unregisterModule('my-module')

To know if a module is already registered:

if (Object.keys(this.$store._modules.root._children).includes('my-module')) { // module is registered
}

My time is very limited but I am glad that I found some time to write this article (it took me some time though). I hope it helped and you enjoyed reading it.

Discover and read more posts from Roland Doda
get started