Vue 3-tutorial – events emitten van kind naar parent
geverifieerd op 2 september 2026 · 3 min
Een kindcomponent meldt een verandering aan de parent door een event te emitten: defineEmits declareert het, emit('add-to-cart', id) verstuurt het, en de parent luistert ernaar met @add-to-cart. Props dalen af, events stijgen op.
Props dalen af van de parent naar het kind. Om informatie omhoog te sturen emit een component een event en luistert de parent ernaar, precies zoals hij naar een klik zou luisteren.
Het probleem
Sinds de opsplitsing in hoofdstuk 8 leeft de winkelwagen in App.vue en de knop in ProductDisplay.vue. Het kind kan cart niet wijzigen: het heeft er geen toegang toe, en een component hoort niet in de state van een ander te schrijven.
De juiste formulering is niet ‘het kind voegt toe aan de winkelwagen’, maar ‘het kind meldt dat er geklikt is, de parent beslist wat daarmee gebeurt’.
Declareren en emitten
Declareer in het kind de events die het uitstuurt, en emit daarna:
<script setup>
const emit = defineEmits(['add-to-cart'])
function addToCart() {
emit('add-to-cart')
}
</script>
<template>
<button
class="button"
:class="{ disabledButton: !inStock }"
:disabled="!inStock"
@click="addToCart"
>
Ajouter au panier
</button>
</template>defineEmits is een macro van de compiler, net als defineProps: geen import nodig. Ze geeft de functie terug waarmee je emit.
Events declareren is niet verplicht, maar wel nuttig: de lijst documenteert het contract van het component, je editor vult hem automatisch aan, en Vue waarschuwt zodra je een niet-gedeclareerde naam emit.
Luisteren bij de parent
Naar een eigen event luister je met dezelfde syntaxis als naar een native event:
<script setup>
import { ref } from 'vue'
import ProductDisplay from './components/ProductDisplay.vue'
const cart = ref(0)
function updateCart() {
cart.value += 1
}
</script>
<template>
<div class="cart">Panier ({{ cart }})</div>
<ProductDisplay :premium="premium" @add-to-cart="updateCart" />
</template>Klik maar: de teller loopt weer. De knop zit in het kind, de teller in de parent, en geen van beide kent de state van de ander.
Data meesturen
Een realistische winkelwagen onthoudt wát je erin legt, niet alleen hoeveel. Geef de id van de variant mee als tweede argument van emit:
<script setup>
function addToCart() {
emit('add-to-cart', variants.value[selectedVariant.value].id)
}
</script>De parent krijgt hem binnen als parameter van zijn functie:
<script setup>
const cart = ref([])
function updateCart(id) {
cart.value.push(id)
}
</script>
<template>
<div class="cart">Panier ({{ cart.length }})</div>
<ProductDisplay :premium="premium" @add-to-cart="updateCart" />
</template>cart is een array geworden, de weergave gebruikt dus cart.length. Voeg het blauwe T-shirt een paar keer toe en controleer in de Vue-devtools dat de array zich netjes vult met 2234.
Voor meerdere gegevens emit je liever een object dan een lange rij argumenten:
emit('add-to-cart', { id: variant.id, taille: 'M', quantite: 1 })De naam van de events
Schrijf de naam in kebab-case: add-to-cart, niet addToCart. HTML-attributen maken geen onderscheid tussen hoofd- en kleine letters, een template die rechtstreeks in een pagina staat zou @addtocart binnenkrijgen en de listener zou nooit afgaan. In een .vue-bestand herstelt Vue de casing, maar je leert je beter meteen de juiste gewoonte aan.
Een eigen event gaat alleen naar de directe parent. Zitten er drie niveaus tussen de zender en de ontvanger, dan moet je het op elke verdieping opnieuw emitten. Wordt dat vervelend, dan is dat het teken dat je gedeelde state nodig hebt: precies waar Pinia voor dient.
De richting van het verkeer
Een beoordeling die langs deze weg binnenkomt, kun je vervolgens bij het tonen animeren.
Onthoud deze twee zinnen: props dalen af, events stijgen op. De parent bezit de data, het kind vraagt om de wijziging. Dat maakt de flow voorspelbaar en de oorsprong van een verandering altijd traceerbaar.
Events declareer je in de optie emits en verstuur je met this.$emit('add-to-cart', id). De parent luistert er op precies dezelfde manier naar.
remove-from-cart toe dat door een tweede knop wordt uitgestuurd, en laat de parent de laatste id uit de array halen.Veelgemaakte fouten
@addtocart binnenkrijgen en de listener zou nooit afgaan. Gebruik kebab-case.