defineEmits()
The defineEmits()
macro is used to declare and validate events emitted from a child to parent component. Single or multiple events can be listed in an array or, use the object syntax for event validation.
It is only available to use inside of <script setup>
, and importing is not required. Using defineEmits()
also provides an easy to read overview of the components event handling.
If not using
<script setup>
, the equivilent is to use theemits
option inside ofexport default
.
Syntax
// Array syntax
const emit = defineEmits(['event1', 'event2'])
// Object syntax
const emit = defineEmits({
eventName: (payload) => {
// validation
return true; // or false to prevent emitting event
}
});
Parameters:
- Array: An array of strings containing event names (simple syntax).
- Object: An object containing events, each key is an event name. Accepting a function that validates the event payload and returns a boolean.
Basic usage
Here's a simple example of defining emits for a button component, using the array syntax. This component emits a click
event when the button is clicked:
<script setup>
const emit = defineEmits(['click', 'hover']);
const handleClick = () => {
emit('click', { timestamp: Date.now() });
};
const handleHover = () => {
emit('hover');
};
</script>
<template>
<button @click="handleClick" @mouseenter="handleHover">
Click me
</button>
</template>
The events are listened to in the parent component:
<template>
<CustomButton
@click="handleButtonClick"
@hover="handleButtonHover"
/>
</template>
<script setup>
const handleButtonClick = (payload) => {
console.log('Button clicked at:', payload.timestamp);
};
const handleButtonHover = () => {
console.log('Button hovered');
};
</script>
The
emit()
function accepts two parameters. The event name (string) and an optional payload. The parent component can listen to these events using the@eventName
syntax orv-on:eventName
.
Type validation
Vue provides type checking for emitted events. This can be useful to ensure the correct data types are emitted from a component:
const emit = defineEmits({
submit: (payload) => {
// Check payload is an object
// amd includes email and password in the payload
return payload && typeof payload === 'object' &&
'email' in payload && 'password' in payload;
},
update: (payload) => {
// Check payload is a number
return typeof payload === 'number' && payload >= 0;
}
});
Event validation
You can add custom validation, and these functions will return a boolean value. This example checks the email and password are structured correctly before emitting to the parent component:
const emit = defineEmits({
submit: (payload) => {
if (!payload.email || !payload.email.includes('@')) {
console.warn('Invalid email provided');
return false;
}
if (!payload.password || payload.password.length < 8) {
console.warn('Password must be at least 8 characters');
return false;
}
return true;
},
});
Multiple event types
Multiple events can have different validation rules:
const emit = defineEmits({
// Simple event with no validation
close: () => true,
// Payload must be an object, with 'data' and 'timestamp' present
save: (payload) => {
return payload && typeof payload === 'object' &&
'data' in payload && 'timestamp' in payload;
},
// More complex validation
delete: (payload) => {
if (!payload.id) {
console.error('An ID is required to delete');
return false;
}
if (payload.confirm !== true) {
console.warn('Confirm to delete');
return false;
}
return true;
}
});
Using with TypeScript
When using TypeScript, you can also define emits with types:
const emit = defineEmits<{
(e: 'update', value: string): void
(e: 'delete', id: number): void
(e: 'submit', data: { email: string; password: string }): void
}>();
// Usage
emit('update', 'new value');
emit('delete', 123);
emit('submit', { email: 'user@example.com', password: 'secure123' });
Practical example
The following example shows how defineEmits()
can be used in a form component. The form events call various functions, these provide validation before emitting the custom event.
<script setup>
const emit = defineEmits({
submit: (payload) => {
return payload && payload.email && payload.password;
},
cancel: () => true,
validationError: (payload) => {
return payload && Array.isArray(payload.errors);
}
});
const formData = ref({
email: '',
password: ''
});
const errors = ref([]);
const handleSubmit = () => {
errors.value = [];
if (!formData.value.email) {
errors.value.push('Email required');
}
if (!formData.value.password) {
errors.value.push('Password required');
}
if (errors.value.length > 0) {
emit('validationError', { errors: errors.value });
return;
}
emit('submit', { ...formData.value });
};
const handleCancel = () => {
emit('cancel');
};
</script>
<template>
<form @submit.prevent="handleSubmit">
<input v-model="formData.email" type="email" placeholder="Email" />
<input v-model="formData.password" type="password" placeholder="Password" />
<button type="submit">Submit</button>
<button type="button" @click="handleCancel">Cancel</button>
</form>
</template>
The parent component listens for these custom events and calls required functions:
<template>
<SignUpForm
@submit="handleFormSubmit"
@cancel="handleFormCancel"
@validation-error="handleValidationError"
/>
</template>
<script setup>
const handleFormSubmit = (data) => {
console.log('Form submitted:', data);
};
const handleFormCancel = () => {
console.log('Form cancelled');
};
const handleValidationError = (payload) => {
console.log('Validation errors:', payload.errors);
};
</script>