Vue $emit() Method

With the built-in $emit() method in Vue we can create a custom event in the child component that can be captured in the parent element.

Props are used to send data from the parent element to the child component, and $emit() is used to do the oposite: to pass information from the child component to the parent.

The purpose of the things we will do next is to end up with the 'favorite' status of a food item to be changed in the parent App.vue instead of in the the FoodItem.vue child component where the change is currently happening.

The reason for changing the favorite status in App.vue instead of in FoodItem.vue is that App.vue is where the favorite status is stored in the first place, so that needs to be updated. In a larger project the data might come from a database we have connection to in App.vue, and we want a change happening from the component to make a change in the database, so we need to communicate back to the parent from the child component.

Emit a Custom Event

There is a need to send information from the component to the parent, and we use the built-in method $emit() to do that.

We already have the toggleFavorite method inside the FoodItem.vue component that runs when the toggle button is clicked. Now let's remove the existing line and add a line to emit our custom event 'toggle-favorite':

FoodItem.vue:

methods: {
  toggleFavorite() {
    this.foodIsFavorite = !this.foodIsFavorite;
    this.$emit('toggle-Favorite');
  }
}

We can choose the name of our custom event, but it is normal to use kebab-case for emit events.


Receive an Emit Event

The custom emit event 'toggle-favorite' is now emitted from the FoodItem.vue component, but we need to listen to the event in the App.vue parent and call a method that does something so that we can see that the event happened.

We listen to the event with the shorthand @ instead of v-on: in App.vue where the component is created:

Example

Listen to the 'toggle-favorite' event in App.vue:

<food-item
  v-for="x in foods"
  :key="x.name"
  :food-name="x.name"
  :food-desc="x.desc"
  :is-favorite="x.favorite"
  @toggle-favorite="receiveEmit"
/>

When our custom 'toggle-favorite' event happens, we need to create the 'testEmit' method in App.vue so that we can see that the event happened:

methods: {
  receiveEmit() {
    alert('Hello World!');
  }
}
Run Example »

Change The Food Item 'favorite' Status in The Parent

We now have an event that notifies App.vue when the 'Favorite' button is clicked from the child component.

We want to change the 'favorite' property in the 'foods' array in App.vue for the correct food item when a 'Favorite' button is clicked. To do that we send the food item name from FoodItem.vue to App.vue because that is unique for each food item:

FoodItem.vue:

methods: {
  toggleFavorite() {
    this.$emit('toggle-favorite', this.foodName);
  }
}

We can now receive the food item name in App.vue as an argument to the method called when the 'toggle-favorite' event happens, like this:

Example

App.vue:

methods: {
  receiveEmit(foodId) {  
    alert( 'You clicked: ' + foodId );
  }
}
Run Example »

Now that we know what food item that was clicked we can update the 'favorite' status for the correct food item inside the 'foods' array:

App.vue:

methods: {
  receiveEmit(foodId) {
    const foundFood = this.foods.find(
      food => food.name === foodId
    );
    foundFood.favorite = !foundFood.favorite;
  }
}

In the code above, the array method 'find' goes through the 'foods' array and looks for an object with name property equal to the food item we have clicked, and returns that object as 'foundFood'. After that we can set 'foundFood.health' to be oposite to what it was before so that it toggles between true and false.

Learn more about the JavaScript array method 'find' here.

Learn more about JavaScript arrow functions here.

The correct food inside the 'foods' array now gets its 'favorite' status updated. The only thing remaining is to get the image indicating favorite food updated.

Because the food item components are already created with the 'favorite' status from the 'foods' array and sent as a prop 'is-favorite' from App.vue, we just need to refer to this 'isFavorite' prop in FoodItem.vue from v-show where the <img> element is to update the image:

<img src="/img_quality.svg" v-show="isFavorite">

We can also delete the 'foodIsFavorite' data property in FoodItem.vue because it is no longer in use.

Example

In this final example code the favorite status of the food items can be toggled in a similar way as before, but now the favorite status is modified in the correct place, inside App.vue.

Run Example »

The 'emits' Option

In the same way that we declare props inside the FoodItem.vue component, we can also document what the component emits by using the Vue 'emits' option.

Props must be declared in the component, while emits are just recommended to be documented.

This is how we can document our emit in the FoodItem.vue component:

<script>
export default {  
  props: ['foodName','foodDesc','isFavorite'],
  emits: ['toggle-favorite'],
  methods: {
    toggleFavorite() {
      this.$emit('toggle-favorite', this.foodName);
    }
  }
};
</script>

The component becomes easier for others to use when the emits are documented.


Vue Exercises

Test Yourself With Exercises

Exercise:

Props are used to send data 
from the parent element to the child component, 
and  is used to to pass information 
from the child component to the parent.

Start the Exercise



Copyright 1999-2023 by Refsnes Data. All Rights Reserved.