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
.
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.