Events from a Component
The menu items have Order buttons now, and the order lives in App.vue: the ref order and the function addToOrder(id), which adds the item with that id to it, or one more of it when it's there already. But the buttons are in MenuItem, and a component can't change its parent's data: it doesn't have order, and its props are read-only.
So the component tells its parent that something happened, with an event, just as a button tells you about a click with click. It declares its events with defineEmits, which needs no import, just like defineProps. And it sends one with the function that defineEmits returns, usually named emit:
<script setup>
defineProps({
item: { type: Object, required: true },
});
const emit = defineEmits(["order"]);
</script>
<template>
<li class="item">
<span>{{ item.name }}</span>
<button @click="emit('order', item.id)">Order</button>
</li>
</template>emit('order', item.id) sends the event order (Vue says that the component emits it) with a value, the item's id. The parent listens to it as to any other event, with @:
<MenuItem v-for="item in menu" :key="item.id" :item="item" @order="addToOrder" />When the event comes, Vue calls addToOrder with the event's value, so id is the id of the item whose button was clicked. The component doesn't know what the parent does with it. It just says “this item was ordered”, and the parent decides what happens.
emit works in the script too, for example in a function that the button calls. In the script, the props are in the object that defineProps returns:
const props = defineProps({
item: { type: Object, required: true },
});
const emit = defineEmits(["order"]);
function order() {
emit("order", props.item.id);
}Typical mistakes:
- An event that the parent listens to under another name, for example
@orderedforemit('order'), never arrives. Nothing happens, and the console doesn't say anything either, so check that the names match. - An event missing in
defineEmitsstill works, but when the component emits it, Vue warns:[Vue warn]: Component emitted event "order" but it is neither declared in the emits option nor as an "onOrder" prop.Declare every event: the list tells anyone who reads the component what it can say.
Instructions
- 1
In
MenuItem.vue, declare the eventorder, and emit it with the item'sidwhen the Order button is clicked. - 2
In
App.vue, listen to the event onMenuItemand handle it withaddToOrder. Order a Cappuccino and two Carrot Cakes: the order showsCappuccino ×1andCarrot Cake ×2.