Pulled ALL skills from 15 source repositories: - anthropics/skills: 16 (docs, design, MCP, testing) - obra/superpowers: 14 (TDD, debugging, agents, planning) - coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth) - better-auth/skills: 5 (auth patterns) - vercel-labs/agent-skills: 5 (React, design, Vercel) - antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo) - Plus 13 individual skills from various repos Mosaic Stack is not limited to coding — the Orchestrator and subagents serve coding, business, design, marketing, writing, logistics, analysis, and more. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
191 lines
4.6 KiB
Markdown
191 lines
4.6 KiB
Markdown
---
|
|
title: Use Event Validation for Complex Payloads
|
|
impact: LOW
|
|
impactDescription: Event validation catches payload errors early with console warnings during development
|
|
type: best-practice
|
|
tags: [vue3, emits, defineEmits, validation, debugging]
|
|
---
|
|
|
|
# Use Event Validation for Complex Payloads
|
|
|
|
**Impact: LOW** - Vue allows you to validate event payloads using object syntax for `defineEmits`. When a validation function returns `false`, Vue logs a console warning. This helps catch bugs early during development, especially for events with complex payload requirements.
|
|
|
|
## Task Checklist
|
|
|
|
- [ ] Use object syntax for `defineEmits` when validation is needed
|
|
- [ ] Return `true` for valid payloads, `false` for invalid
|
|
- [ ] Add meaningful console warnings in validators
|
|
- [ ] Consider TypeScript for compile-time validation instead
|
|
|
|
## Basic Validation
|
|
|
|
**Using object syntax with validators:**
|
|
```vue
|
|
<script setup>
|
|
const emit = defineEmits({
|
|
// No validation - just declaration
|
|
cancel: null,
|
|
|
|
// Validate that email is present
|
|
submit: (payload) => {
|
|
if (!payload || !payload.email) {
|
|
console.warn('Submit event requires an email field')
|
|
return false
|
|
}
|
|
if (!payload.email.includes('@')) {
|
|
console.warn('Submit event requires a valid email')
|
|
return false
|
|
}
|
|
return true
|
|
},
|
|
|
|
// Validate ID is a number
|
|
select: (id) => {
|
|
if (typeof id !== 'number') {
|
|
console.warn('Select event requires a numeric ID')
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
})
|
|
|
|
function handleSubmit(formData) {
|
|
// If validation fails, Vue logs warning but event still emits
|
|
emit('submit', formData)
|
|
}
|
|
</script>
|
|
```
|
|
|
|
## What Happens on Validation Failure
|
|
|
|
```vue
|
|
<script setup>
|
|
const emit = defineEmits({
|
|
submit: (data) => {
|
|
if (!data?.email) {
|
|
return false // Validation fails
|
|
}
|
|
return true
|
|
}
|
|
})
|
|
|
|
function badSubmit() {
|
|
emit('submit', {}) // Missing email
|
|
// Console: [Vue warn]: Invalid event arguments: event validation failed for event "submit"
|
|
// Event STILL fires - validation is advisory only
|
|
}
|
|
</script>
|
|
```
|
|
|
|
**Important:** Validation failure only logs a warning. The event still emits. This is intentional for development debugging, not runtime enforcement.
|
|
|
|
## Common Validation Patterns
|
|
|
|
### Required Fields
|
|
```vue
|
|
const emit = defineEmits({
|
|
'user-created': (user) => {
|
|
const required = ['id', 'name', 'email']
|
|
const missing = required.filter(field => !user?.[field])
|
|
if (missing.length) {
|
|
console.warn(`user-created missing fields: ${missing.join(', ')}`)
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
})
|
|
```
|
|
|
|
### Type Checking
|
|
```vue
|
|
const emit = defineEmits({
|
|
'page-change': (page) => typeof page === 'number' && page > 0,
|
|
|
|
'items-selected': (items) => Array.isArray(items),
|
|
|
|
'filter-applied': (filter) => {
|
|
return filter && typeof filter.field === 'string' && filter.value !== undefined
|
|
}
|
|
})
|
|
```
|
|
|
|
### Range Validation
|
|
```vue
|
|
const emit = defineEmits({
|
|
'rating-change': (rating) => {
|
|
if (typeof rating !== 'number' || rating < 1 || rating > 5) {
|
|
console.warn('Rating must be a number between 1 and 5')
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
})
|
|
```
|
|
|
|
## TypeScript Alternative
|
|
|
|
For compile-time validation, prefer TypeScript types over runtime validators:
|
|
|
|
```vue
|
|
<script setup lang="ts">
|
|
interface SubmitPayload {
|
|
email: string
|
|
password: string
|
|
rememberMe?: boolean
|
|
}
|
|
|
|
const emit = defineEmits<{
|
|
submit: [payload: SubmitPayload]
|
|
cancel: []
|
|
'page-change': [page: number]
|
|
}>()
|
|
|
|
// TypeScript catches errors at compile time
|
|
emit('submit', { email: 'test@test.com' })
|
|
// Error: Property 'password' is missing
|
|
</script>
|
|
```
|
|
|
|
TypeScript validation is:
|
|
- Caught at compile time, not runtime
|
|
- Provides IDE autocompletion
|
|
- Zero runtime overhead
|
|
|
|
## When to Use Runtime Validation
|
|
|
|
Use object syntax validation when:
|
|
- You're not using TypeScript
|
|
- You need to validate values that can't be expressed in types (ranges, formats)
|
|
- You want runtime debugging help during development
|
|
- You're building a component library and want helpful dev warnings
|
|
|
|
## Combining Both Approaches
|
|
|
|
```vue
|
|
<script setup lang="ts">
|
|
interface FormData {
|
|
email: string
|
|
age: number
|
|
}
|
|
|
|
// TypeScript handles type structure
|
|
// Runtime validator handles value constraints
|
|
const emit = defineEmits({
|
|
submit: (data: FormData) => {
|
|
if (data.age < 0 || data.age > 150) {
|
|
console.warn('Age must be between 0 and 150')
|
|
return false
|
|
}
|
|
if (!data.email.includes('@')) {
|
|
console.warn('Invalid email format')
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
})
|
|
</script>
|
|
```
|
|
|
|
## Reference
|
|
- [Vue.js Component Events - Events Validation](https://vuejs.org/guide/components/events.html#events-validation)
|