FE Engineer @
Creator of
ee-validate
<input
type="text"
v-validate="'required'"
/>
<ValidationProvider
rules="required"
v-slot="{ errors }"
>
<input
type="text"
v-model="input"
/>
</ValidationProvider>
📉 Learning curve
🐢 Slow (Fast Enough™)
🎯 Missed opportunities
😣 Verbose by default
✌ Vue 3 Support
✨ Better DX
⚡ Faster Forms
<template>
<form>
<input v-model="name" type="text">
<input v-model="email" type="email">
<input v-model="password" type="password">
</form>
</template>
<script>
export default {
data: () => ({
name: '',
email: '',
password: ''
})
};
</script>
👈 This is redundant
<ValidationObserver
as="form"
@submit="onSubmit"
v-slot="{ errors }"
>
<ValidationProvider
name="email"
as="input"
type="email"
rules="required|email"
/>
{{ errors.email }}
<button>Submit</button>
</ValidationObserver>
Added `as` prop on both Provider and Observer components
<ValidationObserver
as="form"
@submit="onSubmit"
v-slot="{ errors }"
>
<ValidationProvider
name="email"
as="input"
type="email"
rules="required|email"
/>
{{ errors.email }}
<button>Submit</button>
</ValidationObserver>
Direct access to errors from the observer slot
<ValidationProvider
name="email"
rules="required|email"
v-slot="{ field, errors }">
<input v-bind="field" type="text" />
{{ errors[0] }}
</ValidationProvider>
v-model is no longer required
<ValidationProvider rules="required" name="birthday" v-slot="v">
<v-menu
ref="menu"
v-model="menu"
:close-on-content-click="false"
:return-value.sync="date"
transition="scale-transition"
offset-y
min-width="290px"
>
<template v-slot:activator="{ on }">
<v-text-field
v-model="date"
label="Picker in menu"
prepend-icon="event"
readonly
v-on="on"
></v-text-field>
</template>
<v-date-picker v-model="date" no-title scrollable>
<v-spacer></v-spacer>
<v-btn text color="primary" @click="menu = false">Cancel</v-btn>
<v-btn text color="primary" @click="$refs.menu.save(date)">OK</v-btn>
</v-date-picker>
</v-menu>
</ValidationProvider>
Which node is an input? 😵
<ValidationProvider rules="required" name="birthday" v-slot="v">
<VNode
v-model="something"
>
<VNode v-model="something" />
<VNode v-model="something" />
</VNode>
</ValidationProvider>
This is how it looks like from our code POV
<ValidationProvider rules="required" name="birthday" v-slot="{ field }">
<v-menu
ref="menu"
v-model="menu"
:close-on-content-click="false"
:return-value.sync="date"
transition="scale-transition"
offset-y
min-width="290px"
>
<template v-slot:activator="{ on }">
<v-text-field
v-model="date"
v-bind="field"
label="Picker in menu"
prepend-icon="event"
readonly
v-on="on"
></v-text-field>
</template>
<v-date-picker v-model="date" v-bind="field" no-title scrollable>
<v-spacer></v-spacer>
<v-btn text color="primary" @click="menu = false">Cancel</v-btn>
<v-btn text color="primary" @click="$refs.menu.save(date)">OK</v-btn>
</v-date-picker>
</v-menu>
</ValidationProvider>
<template>
<ValidationObserver as="form" @submit="onSubmit">
<!-- Some Fields -->
</ValidationObserver>
</template>
<script>
export default {
setup() {
const onSubmit = (values) => {
// Send form values to the API
// This will never run unless all fields are valid!
};
return {
onSubmit
};
}
};
</script>
automatic validation before submission
<template>
<div>
<input type="text" name="email" v-model="value">
{{ errorMessage }}
</div>
</template>
<script>
import { useField } from 'vee-validate';
export default {
setup() {
const { value, errorMessage } = useField('email', 'required|email');
return {
value,
errorMessage
};
}
};
</script>
Creates validatable models
<template>
<div>
<form @submit="onSubmit">
<input name="name" v-model="name" type="text" />
<span>{{ nameErrors[0] }}</span>
<input name="email" v-model="email" type="email" />
<span>{{ emailErrors[0] }}</span>
<input name="password" v-model="password" type="password" />
<span>{{ passwordErrors[0] }}</span>
<button>Submit</button>
</form>
</div>
</template>
<script>
import { useField, useForm } from 'vee-validate';
export default {
setup() {
const { form, handleSubmit } = useForm();
const { value: name, errors: nameErrors } = useField('name', 'required', { form });
const { value: password, errors: passwordErrors } = useField('password', 'required|min:8', { form });
const { value: email, errors: emailErrors } = useField('email', 'required|email', { form });
const onSubmit = handleSubmit(values => {
// Send data to the API
console.log(values);
});
return {
name,
nameErrors,
password,
passwordErrors,
email,
emailErrors,
onSubmit
};
}
};
</script>
Creates a container for fields
import { extend } from 'vee-validate';
extend('required', (value) => {
return !!value;
});
You can define them globally with the `extend` function
import { useField } from 'vee-validate';
export default {
setup() {
const { value: password, errors: passwordErrors } = useField(
'password',
(value) => {
if (!value) {
return 'This is required';
}
if (value.length <= 8) {
return 'This is too short';
}
return true;
}
);
return { password, passwordErrors };
}
};
You can pass validator functions to useField
<template>
<ValidationObserver as="form" v-slot="{ errors }">
<ValidationProvider as="input" name="password" type="password" :rules="pwRules" />
{{ errors.password }}
</ValidationObserver>
</template>
<script>
export default {
setup() {
const pwRules = (value) => {
if (!value) {
return 'This is required';
}
if (value.length <= 8) {
return 'This is too short';
}
return true;
};
return { pwRules };
}
};
</script>
You can pass them to the rules prop
import { useField } from 'vee-validate';
import * as yup from 'yup';
export default {
setup() {
const pwValidation = yup.string().required().min('8');
const rules = async (value) => {
try {
await pwValidation.validate(value);
return true;
} catch (err) {
return err.message;
}
}
const { value, errors } = useField('password', rules);
return {
value,
errors
};
}
};
Use any third party tools, like `yup`:
import * as yup from 'yup';
import { useField } from 'vee-validate';
import { wrap } from '@vee-validate/yup';
export default {
setup() {
const pwValidator = wrap(yup.string().required().min('8'));
const { value, errors } = useField('password', pwValidator);
return {
value,
errors
};
}
};
Using `@vee-validate/yup`
import * as yup from 'yup';
import { wrapSchema } from '@vee-validate/yup';
export default {
setup() {
const schema = yup.object().shape({
name: yup.string().required(),
email: yup.string().email().required(),
password: yup.string().min(8).required()
});
return {
schema: wrapSchema(schema),
onSubmit: (values) => {
console.log(values);
}
}
}
};
You can define rules at the form level
<ValidationObserver
:validationSchema="schema"
as="form" @submit="onSubmit"
v-slot="{ errors }"
>
<ValidationProvider
name="name"
as="input"
/>
<span>{{ errors.name }}</span>
<!-- Other fields... -->
</ValidationObserver>
When its ready™