Table of Contents
URL: https://www.progressiverobot.com/vuejs-model-form-validation-vuelidate/
While the most beginner-friendly approach to form validation in Vue.js might be through template-based forms, a much more flexible way is to validate the model instead. Model-based validation tends to be easier to understand and change for larger apps, moving the visual clutter from the template to the model and reducing it significantly. The most well-known library to accomplish this in <^>Vue<^> is through Vuelidate.
Installation
As usual, <^>Vuelidate<^> can be installed from NPM or Yarn.
# Yarn
$ yarn add vuelidate
# NPM
$ npm install vuelidate --save
Then, in your app bootstrap, enable the <^>Vuelidate<^> plugin.
[label main.js]
import Vue from 'vue';
import Vuelidate from 'vuelidate';
import App from 'App.vue';
Vue.use(Vuelidate);
new Vue({
el: '#app',
render: h => h(App)
});
Field Validation
To validate fields, add a definition object for properties in your <^>data<^> model to the <^>validations<^> property of your component. Then import validation functions from <^>vuelidate/lib/validations<^> (or custom ones you create). You will then be able to bind to the data property via <^>v-model<^> as usual. Validation information will be stored in <^>this.$v[propertyName]<^>.
$v's Schema:
$v[propertyName]: {
$dirty: boolean, // Whether or not this field has been interacted with yet.
$invalid: boolean, // If this field is current valid or invalid, regardless of whether or not it is "dirty"
$error: boolean, // Shortcut for $invalid && $dirty
$pending: boolean, // Whether or not there is a pending validation (for async validations)
$each: object // Holds all the validations for looped models.
[YourValidationName]: boolean // The state of the validations defined on this property validations. (ie. if you have the 'required' validator, there would be a boolean 'required' property here, and so forth.)
[Nested]: object // You can nest values in your model here as well, and the validation properties will be added to each nested field.
}
<template>
<form>
<input type="text" v-model="emailValue"/>
<p v-if="!$v.emailValue.required">The email field is required!</p>
<p v-if="!$v.emailValue.email">The input must be a proper email!</p>
</form>
</template>
<script>
import { required, email } from 'vuelidate/lib/validations'
export default {
data() {
return {
emailValue: ''
}
},
validations: {
emailValue: {
required,
email
}
}
}
</script>
Built-In Validators
<^>Vuelidate<^> provides these validators by default. They can be imported from <^>vuelidate/lib/validators<^>. If you're using Webpack 2 or Rollup with tree shaking, any validators not used in your project will not be bundled with it.
- <^>required()<^> – This field cannot be empty.
- <^>minLength(length: number)<^> – Minimum length of the field. Works on strings and arrays.
- <^>maxLength(length: number)<^> – Maximum length of the field. Works on strings and arrays.
- <^>between(min, max)<^> – Requires a number to be between the minimum and maximum values.
- <^>alpha()<^> – Only allows alphabetic characters.
- <^>alphaNum()<^> – Only allows alphanumeric characters.
- <^>email()<^> – Allow only valid emails.
- <^>sameAs(fieldName: string | getFieldName: function -> string)<^> – Allows you to require the input to be the same as another field, specified by fieldName or a function.
- <^>or(validators…)<^> – Valid when at least one of the specified validators is valid.
- <^>and(validators…)<^> – Valid when all of the specified validators are valid.
Custom Validatiors
A custom validator in Vuelidate is simply a function that returns a boolean or a promise that resolves to a boolean.
If you want to ensure that fields contain only the word "tom", you might write a validator like this.
[label tom-validator.js]
export const TomValidator = (value, component) => {
return value === 'tom';
}
[label MyComponent.vue]
...
<script>
import { TomValidator } from './tom-validator';
export default {
data() {
return {
inputField: ''
}
},
validators: {
inputField: {
TomValidator
}
}
}
</script>
You might want to go a step further and allow a string to be specified that must be matched. In that case, just create a higher-order function that returns the validator function, like so.
[label match-validator.js]
export const MatchValidator = (stringToMatch) => {
return (value, component) => value === stringToMatch;
}
[label MyComponent.vue]
...
<script>
import { MatchValidator } from './match-validator';
export default {
data() {
return {
inputField: ''
}
},
validators: {
inputField: {
MatchValidator: MatchValidator('rupert')
// Only allows inputField to contain 'rupert.' One might Wonder why the field even exists then...
}
}
}
</script>