Vue Events
In Vue, parent components pass data to child components through props, but how do child components send information back to parent components? The answer is using custom events. A child component can emit an event, and the parent component can listen to this event just like listening to native DOM events.
Emitting and Listening to Events
A child component can use the built-in $emit method to trigger an event by passing in the event name.
MyComponent.vue (Child Component)
<script setup>
// Use the defineEmits macro to declare the events this component will emit
const emit = defineEmits(['someEvent'])
function handleClick() {
// Trigger the 'someEvent' event
emit('someEvent')
}
</script>
<template>
<button @click="handleClick">Click me</button>
</template>The parent component can use v-on (abbreviated as @) to listen to events triggered by the child component.
App.vue (Parent Component)
<script setup>
import MyComponent from './MyComponent.vue'
function onSomeEvent() {
console.log('someEvent was emitted!')
}
</script>
<template>
<MyComponent @some-event="onSomeEvent" />
</template>Note that, similar to prop naming conversion, automatic case conversion also exists for event names. Since HTML attributes are case-insensitive, we recommend using kebab-case (hyphen-separated) event listeners. If a camelCase event someEvent is triggered in the child component, it can be listened to with some-event in the parent component.
Passing Arguments
Sometimes, it's useful to pass specific data when emitting an event. For example, we might want the <BlogPost> component to tell the parent component by how much to enlarge the font size of the blog post.
BlogPost.vue (Child Component)
<script setup>
const emit = defineEmits(['enlarge-text'])
function enlargeText() {
// Trigger event and pass a value
emit('enlarge-text', 0.1)
}
</script>
<template>
<button @click="enlargeText">Enlarge text</button>
</template>Then, when the parent component listens to this event, it can receive this value through an inline arrow function. This value will be passed as the first parameter of that function.
App.vue (Parent Component)
<script setup>
import { ref } from 'vue'
import BlogPost from './BlogPost.vue'
const postFontSize = ref(1)
</script>
<template>
<div :style="{ fontSize: postFontSize + 'em' }">
<BlogPost @enlarge-text="(amount) => postFontSize += amount" />
</div>
</template>If you want the event handler to be a method, the passed parameters will be passed as the first parameter of that method.
<script setup>
// ...
function onEnlargeText(amount) {
postFontSize.value += amount
}
</script>
<template>
<BlogPost @enlarge-text="onEnlargeText" />
</template>Event Validation
Similar to props, we can also validate emitted events. To add validation, the event needs to be defined as an object, not a string array.
<script setup>
const emit = defineEmits({
// No validation
click: null,
// Validate the submit event
submit: ({ email, password }) => {
if (email && password) {
return true
} else {
console.warn('Invalid submit event payload!')
return false
}
}
})
function submitForm() {
emit('submit', { email: 'user@example.com', password: '123' })
}
</script>If validation fails, Vue will print a warning in the console.
v-model with Components
Custom events can also be used to create custom input components that support v-model. A v-model on a component uses modelValue as the prop and update:modelValue as the event by default. We can implement both in the child component to make it support v-model.
CustomInput.vue
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template>Now, the parent component can use v-model on CustomInput.
App.vue
<script setup>
import { ref } from 'vue'
import CustomInput from './CustomInput.vue'
const searchText = ref('hello')
</script>
<template>
<CustomInput v-model="searchText" />
<p>{{ searchText }}</p>
</template>