Guide
Basics
As we mentioned in the getting started guide, validation rules are defined inside the object returned by the validations
method. We will refer to those as just validations from now on.
You can access the component's instance, and its properties via this
when writing more complicated validation rules.
WARNING
Note: Pre Vuelidate 2 validations
was allowed to be an object as well as a function. This is still available, for backwards compatibility reasons, but is discouraged. Please migrate to a method instead.
Each validation rule must have a corresponding property inside the data
object.
<script>
import { useVuelidate } from '@vuelidate/core'
import { required } from '@vuelidate/validators'
export default {
setup () {
return {
v$: useVuelidate()
}
},
data () {
return {
name: ''
}
},
validations () {
return {
name: { required }
}
}
}
</script>
Vuelidate comes with a set of validators, that live inside the @vuelidate/validations
package, which you must install separately. For a full list of available validators, check the Validators page.
Validators are functions that receive the matching value and return either a boolean
or an object with a $valid
property. If the validation passes, return true
or { $valid: true }
, false
otherwise.
The built-in validators are additionally wrapped in an object which contains several additional properties like:
$validator
– contains the validation function$message
– contains the validator’s error message$params
– contains the params that are passed during validator configuration (applies to validators that accept configuration)
Here’s an example of how the minLength
validator looks like:
export default function minLength (min) {
return {
$validator: minLength(min),
$message: ({ $params }) => `This field should be at least ${$params.min} long.`,
$params: { min }
}
}
Vuelidate also supports writing your own custom validators, you can learn more about how to write your own validators on the Custom Validators page.
Checking for validation state
Now that we have our validation rules set up, we can start checking for errors.
Vuelidate builds a validation state, that can be accessed via the exposed Vuelidate property (this is the property name that you return from setup
), most commonly called v$
or just v
. It is a nested object that follows your validations
structure, but with some extra validity related properties.
v$
is also reactive - meaning it changes as the user interacts with the state!
Building up on our form example above, to check whether name is valid we can now check to see if the name.$error
property is true
or false
, and display an error for our users.
<label>
<input v-model="name">
<div v-if="v$.name.$error">Name field has an error.</div>
</label>
Using this approach, you can apply error classes, show messages or add attributes.
The properties you will check against most often are:
"$dirty": false,
"$error": false,
"$errors": [],
"$silentErrors": [],
"$invalid": false,
This is just a subset of all the properties, but they are enough to get us started. To see all available properties, check the Validation state API
The dirty state
Vuelidate tracks the $dirty
state of each property. This is used to keep fields from showing as invalid, before the user has interacted with them.
A property is considered $dirty
after it has received some sort of interaction from the user, which means all properties begin with a $dirty
state of false
.
There are two properties, attached to each validation field, that are used to check if said field is valid, $invalid
and $error
.
Although the validations will run in the background updating the $invalid
state, the $error
will stay false
until both the $dirty
and $invalid
states are true
.
There are multiple ways to update the $dirty
state.
Using $touch
You can programmatically update the $dirty
state by calling the $touch
method on a validation field. For example, for the name
field you can call v$.name.$touch()
to change it’s $dirty
state.
Here’s an example of adding it to the above form, where we attach it to the @blur
event so that our error only shows up after the user entered and left the input.
<label>
<input v-model="name" @blur="v$.name.$touch">
<div v-if="v$.name.$error">Name field has an error.</div>
</label>
If you want to change the $dirty
state for more than one property, you can also use v$.$touch()
, which will call $touch
on all the nested properties. $touch
is attached to each field, to its parent, all the way up to the root of the validation.
This is useful in cases where you have a Submit button and want to trigger the whole form $dirty
. See Validating Forms Before Submitting for an example.
Using the $model
property
Another way to ensure that the $dirty
state is updated when a user interacts with an input, is to directly bind your input's v-model
declaration to the $model
of that field's validation object.
This is a special property that acts as a "proxy" to the targeted property. When you modify it, it calls $touch()
for that property and then proceeds to update the original value.
<template>
<input v-model="v$.name.$model">
</template>
<script>
import { useVuelidate } from '@vuelidate/core'
import { required } from '@vuelidate/validators'
export default {
setup: () => ({ v$: useVuelidate() })
data () {
return {
name: ''
}
},
validations () {
return {
name: { required }
}
}
}
</script>
This binding is accomplished internally through a getter/setter that reads and updates the original state. When the setter is called, it also triggers $touch()
for that property.
Setting dirty state with $autoDirty
It is quite common to forget to use $model
or $touch
. If you want to ensure dirty state is always tracked, you can use the $autoDirty
config param, when defining your validation rules.
import { useVuelidate } from '@vuelidate/core'
import { required } from '@vuelidate/validators'
export default {
setup: () => ({ v$: useVuelidate() }),
data () {
return { name: '' }
},
validations () {
return {
name: { required, $autoDirty: true },
}
}
}
This will create an internal watcher, that will update $dirty
, the moment that field property is changed. It will ensure the validator tracks its bound data, and sets the dirty state accordingly.
You can then change your field's v-model
expression to just the data property:
<input v-model="name">
You can pass `$autoDirty` to all validators, by defining it in the global config
Lazy validations
Validation in Vuelidate 2 introduces the $lazy
param, which will make the selected validators lazy. That means they will only be called, after the field is $dirty
, so after touch()
is called, by using that property’s $model
or by also using the $autoDirty
param.
This saves extra invocations for async validators as well as makes the initial validation setup a bit more performant.
import { useVuelidate } from '@vuelidate/core'
import { required } from '@vuelidate/validators'
export default {
setup: () => ({ v$: useVuelidate() }),
data () {
return { name: '' }
},
validations () {
return {
name: { required, $lazy: true },
}
}
}
You can pass `$lazy` to all validators, by defining it in the global config
Resetting dirty state
If you wish to reset a form's $dirty
state, you can do so by using the appropriately named $reset
method. For example when closing a create/edit modal, you don't want the validation state to persist.
<app-modal @closed="v$.$reset()">
<!-- some inputs -->
</app-modal>
Collective properties
The validation state has entries for each validator, that hold a set of helpful properties that we can use to show warnings, add classes and more.
{
"$dirty": false,
"$error": false,
"$errors": [],
"$silentErrors": [],
"$invalid": false,
// ...other properties
"name": {
"$dirty": false,
"required": {
"$message": "Value is required",
"$params": {},
"$pending": false,
"$invalid": false
},
"$invalid": false,
"$pending": false,
"$error": false,
"$errors": [],
"$silentErrors": []
// ...other properties
}
}
As you can see, the name
field has its own $dirty
, $error
among other attributes, as well as an object for the required
validator.
The root properties like, $dirty
, $error
and $invalid
are all collective computed properties, meaning their value changes depending on the nested children's state.
Example: If a form has 10 fields and one of them has its $error: true
, then the root $error
will also be true
, giving additional flexibility when trying to display error state.
Displaying error messages
TIP
All validators in Vuelidate 2 include error messages.
The validation state holds useful data, like the invalid state of each property validator, along with extra properties, like an error message or extra parameters.
Error messages come out of the box with the bundled validators in @vuelidate/validators
package. You can see how to customise error messages at the Custom Validators page
The easiest way to display errors is to use the form's top level $errors
property. It is an array of validation objects, that you can iterate over. Use the $uid
property for your key
.
<p
v-for="error of v$.$errors"
:key="error.$uid"
>
<strong>{{ error.$validator }}</strong>
<small> on property</small>
<strong>{{ error.$property }}</strong>
<small> says:</small>
<strong>{{ error.$message }}</strong>
</p>
You can also check for errors on each form property:
<p
v-for="error of v$.name.$errors"
:key="error.$uid"
>
<!-- Same as above -->
</p>
Validating forms before submitting
To submit a form, you often need to validate it first. It is a good practice to call the $validate
method on the form first, so all validators (including those with $lazy: true
) are run. This will also cause all the errors to show up that were hidden due to $dirty
state being false
.
It returns a promise, which resolves with a boolean when all validators resolve, depending on the validation status.
export default {
methods: {
async submitForm () {
const isFormCorrect = await this.v$.$validate()
// you can show some extra alert to the user or just leave the each field to show it's `$errors`.
if (!isFormCorrect) return
// actually submit form
}
}
}