agm1984
agm1984

Reputation: 17160

How to emit event from child to parent in Vue3 with <script setup> syntax?

I have the following component:

<SomeModal :is-modal-active="isAddingThing" @close="isAddingThing = false" />

Inside that component looks like this:

<script setup>
import { defineProps } from 'vue'

const props = defineProps({
    isModalActive: Boolean,
})

const handleClose = () => {
    emit('close') // doesn't work
}
</script>

<template>
<V-Modal @close="handleClose">
    ...
</V-Modal>
</template>

How do I emit to parent?

Upvotes: 48

Views: 77070

Answers (2)

Daniel
Daniel

Reputation: 35714

Update 2022 - for Vue 3.2 and newer

The useContext and getCurrentInstance APIs were deprecated and are no longer exposed to the Vue 3 API. Instead of that, you should use the defineEmits at the root level on the setup script.

<template>
  <button @click="action('🎉')"></button>
</template>
<script setup>
  const emit = defineEmits(['close', 'unClose'])
  const action = () => emit('close', val);
</script>

and for typescript the definition would be:

const emit = defineEmits<{
  (e: 'close', val: string): void
  (e: 'unClose', id: number): void
}>()

Outdated, Pre-Vue 3.2

  • You don't need to define emit at all to run $emit from the template
  • useContext provides the emit function for use in setup function
  • using defineEmits adds the list of emits to the component (equivalent to emits: definition of component) but doesn't help with the emit

useContext and defineEmits are deprecated as of Vue 3.2, use getCurrentInstance and defineEmits


useContext

<template>
  <button @click="action('🎉')"></button>
  <!-- emit is defined through useContext -->
  <button @click="emit('action1','🥑')">1</button>
  <!-- $emit here doesn't need to be defined -->
  <button @click="$emit('action2','🦄')">2</button>
</template>
<script setup>
  import { defineProps, useContext } from 'vue'
  const { emit } = useContext()
  const action = (id) => emit('action', id);
</script>

Note that vetur extension might tell you that it's deprecated and that you should use useSlots and useAttrs instead. If you want to use emit from the script, those, obviously, won't help with that.

getCurrentInstance

import { defineProps, getCurrentInstance} from 'vue'
const { emit } = getCurrentInstance()

In addition to running an emit, vue3 allows (recommends🤷‍♂️) that the emits are defined, so the component parent might know what events a component might emit. To do that using the <script setup> form, you can use defineEmit

<script setup>
  import { defineEmit } from 'vue'
  const emit = defineEmit(['action'])
  const action = (id) => emit('action', id);
</script>

This will allow sending action emit event, but if you emit a doit although it will still emit, you'll get a warning in the console

[Vue warn]: Component emitted event "doit" but it is neither declared in the emits option nor as an "onDoit" prop.

Upvotes: 34

McGrew
McGrew

Reputation: 952

With Vue version 3.2, if you want to emit an event from inside <script setup>, then all you have to do is define your emits with the defineEmits() method that is automatically available inside <script setup> (you don't have to import it), then you can emit the event by calling emit('myEventName', myParams). Here's some sample code...

<script setup>
const emit = defineEmits(['eventA', 'eventB'])
function btnClick(params) {
    emit('eventA')
    emit('eventB', params)
}
</script>

Upvotes: 66

Related Questions