Passer au contenu

Les événements de composant

Cette page suppose que vous avez déjà lu les principes fondamentaux des composants. Lisez-les d'abord si vous débutez avec les composants.

Émettre et écouter des événements

Un composant peut émettre des événements personnalisés directement à partir du template (par exemple, dans un gestionnaire d'événement v-on) à l'aide de la méthode native $emit :

template
<!-- MyComponent -->
<button @click="$emit('someEvent')">Cliquez-Moi</button>

La méthode $emit() est également disponible sur l'instance du composant avec this.$emit() :

js
export default {
  methods: {
    submit() {
      this.$emit('someEvent')
    }
  }
}

Le composant parent peut alors l'écouter en utilisant v-on :

template
<MyComponent @some-event="callback" />

Le modificateur .once est également pris en charge sur les écouteurs d'événements de composants :

template
<MyComponent @some-event.once="callback" />

Comme les composants et les props, les noms d'événements fournissent une transformation de casse automatique. Notez que nous avons émis un événement camelCase, mais que nous pouvons l'écouter à l'aide d'un écouteur kebab-case dans le parent. Comme pour la casse des props, nous vous recommandons d'utiliser des noms d'écouteurs d'événement au format kebab-case dans les templates.

TIP

Contrairement aux événements DOM natifs, les événements émis par les composants ne se propagent pas au delà de leur parent direct. Vous ne pouvez écouter que les événements émis par un composant enfant direct. S'il est nécessaire de communiquer entre des composants frères ou profondément imbriqués, utilisez un bus d'événements externe ou une solution de gestion d'état global.

Arguments d'événement

Il est parfois utile d'émettre une valeur spécifique avec un événement. Par exemple, nous pouvons vouloir que le composant <BlogPost> soit en charge d'agrandir plus ou moins le texte. Dans ces cas, nous pouvons passer des arguments supplémentaires à $emit pour fournir cette valeur :

template
<button @click="$emit('increaseBy', 1)">
  Increase by 1
</button>

Ensuite, lorsque nous écoutons l'événement dans le composant parent, nous pouvons utiliser une fonction fléchée comme écouteur, ce qui nous permet d'accéder à l'argument de l'événement :

template
<MyButton @increase-by="(n) => count += n" />

Ou, si le gestionnaire d'événements est une méthode :

template
<MyButton @increase-by="increaseCount" />

Ensuite, la valeur sera transmise comme premier paramètre de cette méthode :

js
methods: {
  increaseCount(n) {
    this.count += n
  }
}
js
function increaseCount(n) {
  count.value += n
}

TIP

Tous les arguments supplémentaires passés à $emit() après le nom de l'événement seront transmis à l'écouteur. Par exemple, avec $emit('foo', 1, 2, 3) la fonction d'écoute recevra trois arguments.

Déclaration des événements émis

Les événements émis peuvent être explicitement déclarés sur le composant via la macro defineEmits()l'option emits :

vue
<script setup>
defineEmits(['inFocus', 'submit'])
</script>

La méthode $emit que nous avons utilisée dans le <template> n'est pas accessible dans la section <script setup> d'un composant, mais defineEmits() renvoie une fonction équivalente que nous pouvons utiliser à la place :

vue
<script setup>
const emit = defineEmits(['inFocus', 'submit'])

function buttonClick() {
  emit('submit')
}
</script>

La macro defineEmits() ne peut pas être utilisée dans une fonction, elle doit être placée directement dans <script setup>, comme dans l'exemple ci-dessus.

Si vous utilisez une fonction setup explicite au lieu de <script setup>, les événements doivent être déclarés à l'aide de l'option emits, et la fonction emit est exposée dans le contexte de setup() :

js
export default {
  emits: ['inFocus', 'submit'],
  setup(props, ctx) {
    ctx.emit('submit')
  }
}

Comme pour les autres propriétés du contexte de setup(), emit peut être déstructurée en toute sécurité :

js
export default {
  emits: ['inFocus', 'submit'],
  setup(props, { emit }) {
    emit('submit')
  }
}
js
export default {
  emits: ['inFocus', 'submit']
}

L'option emits et la macro defineEmits() supportent également une syntaxe avec un objet. Si vous utilisez TypeScript, vous pouvez utiliser les arguments, ce qui nous permet d'effectuer une validation à l'exécution du contenu des événements émis :

vue
<script setup lang="ts">
const emit = defineEmits({
  submit(payload: { email: string, password: string }) {
    // renvoie `true` ou `false` pour indiquer
    // que la validation a réussi/échoué
  }
})
</script>

Si vous utilisez TypeScript avec <script setup>, il est également possible de déclarer des événements émis à l'aide d'annotations de type :

vue
<script setup lang="ts">
const emit = defineEmits<{
  (e: 'change', id: number): void
  (e: 'update', value: string): void
}>()
</script>

Plus de détails : Typer les données émises par les composants

js
export default {
  emits: {
    submit(payload: { email: string, password: string }) {
      // renvoie `true` ou `false` pour indiquer
      // que la validation a réussi/échoué
    }
  }
}

Voir également : Typer les données émises par les composants

Bien que facultatif, il est recommandé de définir tous les événements émis afin de mieux documenter le fonctionnement d'un composant. Cela permet également à Vue d'exclure les écouteurs connus des attributs implicitement déclarés (fallthrough attributes), évitant ainsi les problèmes liés aux cas à la marge causés par des événements DOM envoyés manuellement par du code tiers.

TIP

Si un événement natif (par exemple, click) est défini dans l'option emits, l'écouteur n'écoutera alors que les événements click émis par le composant et ne réagira plus aux événements click natifs.

Validation des événements

Semblable à la validation de type de prop, un événement émis peut être validé s'il est défini avec la syntaxe utilisant un objet au lieu d'un tableau.

Pour ajouter une validation, l'événement se voit attribuer une fonction qui reçoit les arguments passés à l'appel de this.$emitemit et renvoie un booléen pour indiquer si l'événement est valide ou non.

vue
<script setup>
const emit = defineEmits({
  // Pas de validation
  click: null,

  // Validation de l'événement soumis
  submit: ({ email, password }) => {
    if (email && password) {
      return true
    } else {
      console.warn('Invalid submit event payload!')
      return false
    }
  }
})

function submitForm(email, password) {
  emit('submit', { email, password })
}
</script>
js
export default {
  emits: {
    // Pas de validation
    click: null,

    // Validation de l'événement soumis
    submit: ({ email, password }) => {
      if (email && password) {
        return true
      } else {
        console.warn('Invalid submit event payload!')
        return false
      }
    }
  },
  methods: {
    submitForm(email, password) {
      this.$emit('submit', { email, password })
    }
  }
}
Les événements de composanta chargé