paint-brush
Learn How to Emit Custom Events in Vue With $emitby@smpnjn
8,983 reads
8,983 reads

Learn How to Emit Custom Events in Vue With $emit

by Johnny SimpsonMay 15th, 2022
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

In Vue, data is typically passed from parent components to their children in a uni-directional fashion. This is transferred with [props], which are the properties or attributes we give to [components] Props allow us to do everything we need to do with data. Sometimes, however, we use `$emit` to send data upwards, and then trigger an event in the parent component should an event be fired. There are three ways to fire events in Vue - depending on if you're using the Options API, Composition API, or inlining your events.

Company Mentioned

Mention Thumbnail
featured image - Learn How to Emit Custom Events in Vue With $emit
Johnny Simpson HackerNoon profile picture


In Vue, data is typically passed from parent components to their children in a uni-directional fashion. This is transferred with props, which are the properties or attributes we give to components.


For example, if we call a component PageOne, which has a prop called name, that name property would become available within the PageOne component itself, letting us do what we want to do with it. That way, the data is passed down to the child component when we declare it in the parent component or page:


How Components work in Vue


In most scenarios, props allow us to do everything we need to do with data. Sometimes, however, we need to emit data upwards - from a child component to its parent. For this, we use $emit, which lets us send data upwards, and then trigger an event in the parent component should an $emit event be fired.


How $emit works in Vue?

There are three ways to fire $emit in Vue, depending on if you're using the Options API, Composition API, or inlining your $emit events.


  • this.$emit within the Options API.
  • $emit if used in your HTML template.
  • defineEmits and emit if used in the Composition API.


Let's take a look at how this works, through a silly example. Let's say we have a counter component, which looks like this:


<template>
    <button @click="$emit('counterEvent')">Click Me</button>
</template>


This component is stored in a file called Counter.vue. Our component can't be changed as it's used in other places, but it does have an $emit event fired any time it is clicked. This is perfect since we can use this in our parent component.


So what if we want to add this component somewhere - for example, in our App.vue file - and use it to display the value of our counter. Let's try doing that now:


<template>
    <h1>{{ counter }}</h1>
  	<Counter @counter-event="incrCounter"/>
</template>

<script>
import Counter from './Counter.vue'
export default {
    // Add our components
  	components: {
      Counter
    },
    // Store our data
    data() {
        return {
            counter: 0
        }
    },
    methods: {
        incrCounter: function() {
            this.counter += 1;
        }
    }
}
</script>


Let's break this down - first of all, we include our Counter. Since there is an $emit event called counterEvent, we can attach that to our Counter HTML. Whenever $emit fires, it'll fire the counterEvent, and thus the function within that property. Here, we run incrCounter anytime counterEvent fires.


By doing that, we can also increase our counter data by 1, since that is what incrCounter does. As such, we've emitted the click event upwards to our parent component.


Why did we use Kebab Case?

You may notice that when we defined our $emit event, we used camel case (counterEvent), but when tracking the event, we used kebab case (counter-event).


In Vue 3, it is fine to use counterEvent and counter-event interchangably since Vue 3 automatically converts counterEvent to counter-event. In Vue 2, this functionality does not exist, so just stick with counter-event for both.


Passing Data with $emit

Let's say instead, we want our components to define how much the counterEvent should increase. If we want to do that, we can pass a second argument to the $emit function, which is the value:


<template>
    <button @click="$emit('counterEvent', 2)">Click Me</button>
</template>


Here, we are passing the value 2 to our counterEvent. Let's go back to our App.vue file. To leverage this value in counterEvent, we need to write it as a function. Below, n is the value:


<template>
    <h1>{{ counter }}</h1>
  	<Counter @counter-event="(n) => incrCounter(n)"/>
</template>

<script>
import Counter from './Counter.vue'
export default {
    // Add our components
  	components: {
      Counter
    },
    // Store our data
    data() {
        return {
            counter: 0
        }
    },
    methods: {
        incrCounter: function(value) {
            this.counter += value;
        }
    }
}
</script>


Now, our counter will increase by the value put in the child component, allowing us to pass data to our parent component as well. As you'd expect, this is not limited to just numbers but can include any data structure - including objects and strings.


Using $emit with the Options API

We've shown a quite simple example, but we could also have written our Counter.vue child component using a function instead. Here is an example with the Options API, using this.$emit:


<template>
    <button @click="emitFunction">Click Me</button>
</template>

<script>
export default {
  emits: [ 'counterEvent' ],
 	methods: {
		emitFunction: function() {
		    this.$emit('counterEvent', 2)
        }
    }
}
</script>


This may prove to be a slightly cleaner way to use $emit, especially if you want to do other things along with using $emit whenever a button is clicked.


Adding your emit events to your prototype

You may note that we also defined our emit event in emits on the prototype. This is good practice for two reasons:

  • It lets you self-document the code by showing which emit events are possible in this component.
  • It helps you keep track of deprecated emits, since Vue will throw an error if an emit event is used but not found in the emits array.


Using $emit with the Composition API

We can use $emit with the Composition API - the only difference is we have to use defineEmits instead.


<template>
    <button @click="emitFunction">Click Me</button>
</template>

<script setup>
import { defineEmits } from 'vue'

const emit = defineEmits(['counterEvent']);
const emitFunction = function() {
    emit('counterEvent', 2)
}
</script>


defineEmits is used to define a full list of all permissible emit events. Here, we only have one, counterEvent. If you had more than one, you could define them as so:


const emit = defineEmits(['counterEvent', 'anotherEvent', 'finalEvent']);


If you use an emit event not listed in defineEmits, Vue will throw a warning, similar to using emits on the Options API. Otherwise, you can then use the emit() function to emit as usual, without needing to use the Options API at all.


Final Thoughts and Best Practices

Emit is a powerful tool for sending data back up to the parent when we have to. This means datastreams can be two-way in Vue. When defining emit code, the two main best practices are:

  • Always define your emit events in either emits or defineEmits, which will help you keep your code clean and well documented.
  • Normal convention in Vue 3 is to use kebab case (this-is-kebab-case) for HTML, and camel case (thisIsCamelCase) in script. As such, it is best to follow this convention here too.




I hope you've enjoyed this guide on how $emit works. Stay tuned for more Vue Content.


Also Published Here