Vue Composition API: Defining emits
Asked Answered
P

5

60

When defining custom events Vue encourages us to define emitted events on the component via the emits option:

app.component('custom-form', {
  emits: ['inFocus', 'submit']
})

Using Vue 3's composition API, when a standalone composition function emits custom events, is it possible to define these in the composition function?

Polyhedron answered 22/1, 2021 at 11:36 Comment(0)
B
34

If you are using script setup you can use defineEmits which is a compiler macros and you don't have to import it:

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

emit('inFocus')
</script>

You can also use an object syntax, which allows performing events validation:

<script setup>
const emit = defineEmits({
  // No validation
  inFocus: null,

  // Validate submit event
  submit: ({ email, password }) => {
    if (email && password) return true
    else return false
  }
})

function submitForm(email, password) {
  emit('submit', { email, password })
}
</script>

Note: the submit event will be emitted regardless of validation but if the validation doesn't pass, you will get a Vue warning:

[Vue warn]: Invalid event arguments: event validation failed for event "submit".

See it live


Typing with TS:

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

The above code snippets are taken from Vue.js Emitting and Listening to Events

Bleareyed answered 29/1, 2022 at 18:19 Comment(2)
If "the submit event will be emitted regardless of validation" what is the point? "if the validation doesn't pass, you will get a Vue warning" What good is the Vue warning in the console log?Shiprigged
this is a copy/paste from the official Vue documentationTopographer
V
80

No, because composition functions are used inside the setup hook which doesn't have access to the other options like methods and emits:

export default defineComponent({
    name: "layout",
    emits: ['showsidebar'],
    setup(props, { emit }) {
        const showSidebar = ref(true);
        const { breakpoints } = useBreakpoint();
        watch(breakpoints, (val) => {
            showSidebar.value = !(val.is === "xs" || val.is === "sm");
            emit('showsidebar',showSidebar.value);
        });
        return {
            showSidebar,
        };
    },
    data() {
        // ...
    },
});

In the example, useBreakpoint offers only some logic that the component could use. If there was a way to define the emits option in the composition function, then the function would always emit the event, even if the function is only used inside the component that defines the handler of the emitted event.

with the new script setup syntax you could do it as follows:

<script setup>
 import { defineEmits,watch,ref } from 'vue'
    
    const emit = defineEmits(['showsidebar'])
    const showSidebar = ref(true);
    const { breakpoints } = useBreakpoint();
    watch(breakpoints, (val) => {
            showSidebar.value = !(val.is === "xs" || val.is === "sm");
            emit('showsidebar',showSidebar.value);
        });
</script>
Ventriloquism answered 22/1, 2021 at 11:52 Comment(0)
S
72

I did it like this with the script setup syntax:

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

    const emit = defineEmits(['close'])

    const handleClose = () => {
        emit('close')
    }
</script>
Sirois answered 5/7, 2021 at 19:41 Comment(5)
As of today this doesn't work with Nuxt3(vue3) due to Vite. __vite_ssr_import_2__.defineEmit is not a functionCata
It work well, but you have a typo. It is not defineEmit but defineEmits as described here v3.vuejs.org/api/sfc-script-setup.html#using-custom-directivesLyon
And also not forget that defineEmits is a compiler macro and no longer needs to be imported.Lyon
How can we listen to the event inside the script when using <script setup>?Gahan
As well, you can get away with no specific defined items (because, maybe your emits are dynamic?); const emit = defineEmits() seems to work fine for me.License
B
34

If you are using script setup you can use defineEmits which is a compiler macros and you don't have to import it:

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

emit('inFocus')
</script>

You can also use an object syntax, which allows performing events validation:

<script setup>
const emit = defineEmits({
  // No validation
  inFocus: null,

  // Validate submit event
  submit: ({ email, password }) => {
    if (email && password) return true
    else return false
  }
})

function submitForm(email, password) {
  emit('submit', { email, password })
}
</script>

Note: the submit event will be emitted regardless of validation but if the validation doesn't pass, you will get a Vue warning:

[Vue warn]: Invalid event arguments: event validation failed for event "submit".

See it live


Typing with TS:

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

The above code snippets are taken from Vue.js Emitting and Listening to Events

Bleareyed answered 29/1, 2022 at 18:19 Comment(2)
If "the submit event will be emitted regardless of validation" what is the point? "if the validation doesn't pass, you will get a Vue warning" What good is the Vue warning in the console log?Shiprigged
this is a copy/paste from the official Vue documentationTopographer
P
11

If you want to get all the context:

    setup(props, context) {
       // console.log(context)
       context.emit("update:modelValue", data)
    },
Psilomelane answered 14/9, 2021 at 6:3 Comment(1)
This works fine with the Composition API for Vue2.Curarize
S
2

You can use this {} to access the context options.

setup(props, {emit} ) {
    emit("update:modelValue", data)
}
Stretcherbearer answered 11/11, 2022 at 12:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.