Vuelidate(0.4.3)

Simple, lightweight model-based validation for Vue.js 2.0

  • Contextified validators
  • Easy to use with custom validators (e.g. Moment.js)
  • Support for function composition
  • Validates different data sources: Vuex getters, computed values, etc.
  • High test coverage

Getting started


Package content

Simple, lightweight model-based validation for Vue.js

You can read the introduction post for more insight on how this solution differs from other validation libraries.

Installation

Package is installable via npm

npm install vuelidate --save

Basic usage

You can import the library and use as a Vue plugin to enable the functionality globally on all components containing validation configuration.

import Vue from 'vue'
import Vuelidate from 'vuelidate'
Vue.use(Vuelidate)

Alternatively it is possible to import a mixin directly to components in which it will be used.

import { validationMixin } from 'vuelidate'

var Component = Vue.extend({
  mixins: [validationMixin],
  validation: { ... }
})

If you prefer using require, it can be used instead of import statements. This works especially great with destructuring syntax.

const { validationMixin, default: Vuelidate } = require('vuelidate')
const { required, minLength } = require('vuelidate/lib/validators')

The browser-ready bundle is also provided in the package.

<script src="vuelidate/dist/vuelidate.min.js"></script>
// global
Vue.use(window.vuelidate.default)

// local mixin
var validationMixin = window.vuelidate.validationMixin

Check out the JSFiddle example which uses this setup.

Examples


Basic form

For each value you want to validate, you have to create a key inside validations options. You can specify when input becomes dirty by using appropriate event on your input box.

import { required, minLength, between } from 'vuelidate/lib/validators'

export default {
  data () {
    return {
      name: '',
      age: 0
    }
  },
  validations: {
    name: {
      required,
      minLength: minLength(4)
    },
    age: {
      between: between(20, 30)
    }
  }
}

div
  .form-group(v-bind:class="{ 'form-group--error': $v.name.$error }")
    label.form__label Name
    input.form__input(v-model.trim="name" @input="$v.name.$touch()")
  span.form-group__message(v-if="!$v.name.required") Field is required
  span.form-group__message(v-if="!$v.name.minLength")
    | Name must have at least {{$v.name.$params.minLength.min}} letters.
  pre
    | name: {{ $v.name }}

  .form-group(v-bind:class="{ 'form-group--error': $v.age.$error }")
    label.form__label Age
    input.form__input(v-model.trim="age" @blur="$v.age.$touch()")
  span.form-group__message(v-if="!$v.age.between")
    | Must be between {{$v.age.$params.between.min}} and {{$v.age.$params.between.max}}
  pre
    | age: {{ $v.age }}

<div>
  <div class="form-group" v-bind:class="{ 'form-group--error': $v.name.$error }">
    <label class="form__label">Name</label>
    <input class="form__input" v-model.trim="name" @input="$v.name.$touch()">
  </div><span class="form-group__message" v-if="!$v.name.required">Field is required</span><span class="form-group__message" v-if="!$v.name.minLength">Name must have at least {{$v.name.$params.minLength.min}} letters.</span>
  <pre>name: {{ $v.name }}</pre>
  <div class="form-group" v-bind:class="{ 'form-group--error': $v.age.$error }">
    <label class="form__label">Age</label>
    <input class="form__input" v-model.trim="age" @blur="$v.age.$touch()">
  </div><span class="form-group__message" v-if="!$v.age.between">Must be between {{$v.age.$params.between.min}} and {{$v.age.$params.between.max}}</span>
  <pre>age: {{ $v.age }}</pre>
</div>

Contextified validators

You can link related fields by contextified validators. An example of this being sameAs builtin validator.

import { required, sameAs, minLength } from 'vuelidate/lib/validators'

export default {
  data () {
    return {
      password: '',
      repeatPassword: ''
    }
  },
  validations: {
    password: {
      required,
      minLength: minLength(6)
    },
    repeatPassword: {
      sameAsPassword: sameAs('password')
    }
  }
}

div
  .form-group(v-bind:class="{ 'form-group--error': $v.password.$error }")
    label.form__label Password
    input.form__input(v-model.trim="password" @input="$v.password.$touch()")
  span.form-group__message(v-if="!$v.password.required") Password is required.
  span.form-group__message(v-if="!$v.password.minLength")
    | Password must have at least {{ $v.password.$params.minLength.min }} letters.

  .form-group(v-bind:class="{ 'form-group--error': $v.repeatPassword.$error }")
    label.form__label Repeat password
    input.form__input(v-model.trim="repeatPassword" @input="$v.repeatPassword.$touch()")
  span.form-group__message(v-if="!$v.repeatPassword.sameAsPassword") Passwords must be identical.
  pre
    | password: {{ $v.password }}
    | repeatPassword: {{ $v.repeatPassword }}

<div>
  <div class="form-group" v-bind:class="{ 'form-group--error': $v.password.$error }">
    <label class="form__label">Password</label>
    <input class="form__input" v-model.trim="password" @input="$v.password.$touch()">
  </div><span class="form-group__message" v-if="!$v.password.required">Password is required.</span><span class="form-group__message" v-if="!$v.password.minLength">Password must have at least {{ $v.password.$params.minLength.min }} letters.</span>
  <div class="form-group" v-bind:class="{ 'form-group--error': $v.repeatPassword.$error }">
    <label class="form__label">Repeat password</label>
    <input class="form__input" v-model.trim="repeatPassword" @input="$v.repeatPassword.$touch()">
  </div><span class="form-group__message" v-if="!$v.repeatPassword.sameAsPassword">Passwords must be identical.</span>
  <pre>password: {{ $v.password }}
repeatPassword: {{ $v.repeatPassword }}</pre>
</div>

Data nesting

You can nest validators to match your data as deep as you want. Parent validator errors out when any of its children reports an error. This might be very useful for overall form validation.

import { required } from 'vuelidate/lib/validators'

export default {
  data () {
    return {
      form: {
        nestedA: '',
        nestedB: ''
      }
    }
  },
  validations: {
    form: {
      nestedA: {
        required
      },
      nestedB: {
        required
      }
    }
  }
}

div
  .form-group(v-bind:class="{ 'form-group--error': $v.form.nestedA.$error }")
    label.form__label Nested A
    input.form__input(v-model.trim="form.nestedA" @input="$v.form.nestedA.$touch()")
  span.form-group__message(v-if="!$v.form.nestedA.required") Field is required.
  .form-group(v-bind:class="{ 'form-group--error': $v.form.nestedB.$error }")
    label.form__label Nested B
    input.form__input(v-model.trim="form.nestedB" @input="$v.form.nestedB.$touch()")
  span.form-group__message(v-if="!$v.form.nestedB.required") Field is required.

  .form-group(v-bind:class="{ 'form-group--error': $v.form.$error }")
  span.form-group__message(v-if="$v.form.$error") Form is invalid.

  pre form: {{ $v.form }}

<div>
  <div class="form-group" v-bind:class="{ 'form-group--error': $v.form.nestedA.$error }">
    <label class="form__label">Nested A</label>
    <input class="form__input" v-model.trim="form.nestedA" @input="$v.form.nestedA.$touch()">
  </div><span class="form-group__message" v-if="!$v.form.nestedA.required">Field is required.</span>
  <div class="form-group" v-bind:class="{ 'form-group--error': $v.form.nestedB.$error }">
    <label class="form__label">Nested B</label>
    <input class="form__input" v-model.trim="form.nestedB" @input="$v.form.nestedB.$touch()">
  </div><span class="form-group__message" v-if="!$v.form.nestedB.required">Field is required.</span>
  <div class="form-group" v-bind:class="{ 'form-group--error': $v.form.$error }"></div><span class="form-group__message" v-if="$v.form.$error">Form is invalid.</span>
  <pre>form: {{ $v.form }}</pre>
</div>

Validation Groups

If you want to create a validator that groups many otherwise unrelated fields together, you can create a validation group.

import { required } from 'vuelidate/lib/validators'

export default {
  data () {
    return {
      flatA: '',
      flatB: '',
      forGroup: {
        nested: ''
      }
    }
  },
  validations: {
    flatA: { required },
    flatB: { required },
    forGroup: {
      nested: { required }
    },
    validationGroup: ['flatA', 'flatB', 'forGroup.nested']
  }
}

div
  .form-group(v-bind:class="{ 'form-group--error': $v.flatA.$error }")
    label.form__label Flat A
    input.form__input(v-model.trim="flatA" @input="$v.flatA.$touch()")
  span.form-group__message(v-if="!$v.flatA.required") Field is required.
  .form-group(v-bind:class="{ 'form-group--error': $v.flatB.$error }")
    label.form__label Flat B
    input.form__input(v-model.trim="flatB" @input="$v.flatB.$touch()")
  span.form-group__message(v-if="!$v.flatB.required") Field is required.
  .form-group(v-bind:class="{ 'form-group--error': $v.forGroup.nested.$error }")
    label.form__label Nested field
    input.form__input(v-model.trim="forGroup.nested" @input="$v.forGroup.nested.$touch()")
  span.form-group__message(v-if="!$v.forGroup.nested.required") Field is required.

  .form-group(v-bind:class="{ 'form-group--error': $v.validationGroup.$error }")
  span.form-group__message(v-if="$v.validationGroup.$error") Group is invalid.

  pre
    | validationGroup: {{ $v.validationGroup }}

<div>
  <div class="form-group" v-bind:class="{ 'form-group--error': $v.flatA.$error }">
    <label class="form__label">Flat A</label>
    <input class="form__input" v-model.trim="flatA" @input="$v.flatA.$touch()">
  </div><span class="form-group__message" v-if="!$v.flatA.required">Field is required.</span>
  <div class="form-group" v-bind:class="{ 'form-group--error': $v.flatB.$error }">
    <label class="form__label">Flat B</label>
    <input class="form__input" v-model.trim="flatB" @input="$v.flatB.$touch()">
  </div><span class="form-group__message" v-if="!$v.flatB.required">Field is required.</span>
  <div class="form-group" v-bind:class="{ 'form-group--error': $v.forGroup.nested.$error }">
    <label class="form__label">Nested field</label>
    <input class="form__input" v-model.trim="forGroup.nested" @input="$v.forGroup.nested.$touch()">
  </div><span class="form-group__message" v-if="!$v.forGroup.nested.required">Field is required.</span>
  <div class="form-group" v-bind:class="{ 'form-group--error': $v.validationGroup.$error }"></div><span class="form-group__message" v-if="$v.validationGroup.$error">Group is invalid.</span>
  <pre>validationGroup: {{ $v.validationGroup }}</pre>
</div>

Collections validation

Array support with $each keyword

import { required, minLength } from 'vuelidate/lib/validators'

export default {
  data () {
    return {
      people: [{
        name: 'John'
      }, {
        name: ''
      }]
    }
  },
  validations: {

    people: {
      required,
      minLength: minLength(3),
      $each: {
        name: {
          required,
          minLength: minLength(2)
        }
      }
    }

  }
}

div
  div(v-for="(person, index) in people")
    .form-group(v-bind:class="{ 'form-group--error': $v.people.$each[index].$error }")
      label.form__label Name for {{ index }}
      input.form__input(v-model.trim="person.name" @input="$v.people.$each[index].name.$touch()")
    span.form-group__message(v-if="!$v.people.$each[index].name.required") Name is required.
    span.form-group__message(v-if="!$v.people.$each[index].name.minLength")
      | Name must have at least {{ $v.people.$each[index].name.$params.minLength.min }} letters.

  div
    button.button(@click="people.push({name: ''})") Add
    button.button(@click="people.pop()") Remove
  .form-group(v-bind:class="{ 'form-group--error': $v.people.$error }")
  span.form-group__message(v-if="!$v.people.minLength")
    | List must have at least {{ $v.people.$params.minLength.min }} elements.
  span.form-group__message(v-else-if="!$v.people.required") List must not be empty.
  span.form-group__message(v-else-if="$v.people.$error") List is invalid.
  button.button(@click="$v.people.$touch") $touch
  button.button(@click="$v.people.$reset") $reset

  pre
    | people: {{ $v.people }}

<div>
  <div v-for="(person, index) in people">
    <div class="form-group" v-bind:class="{ 'form-group--error': $v.people.$each[index].$error }">
      <label class="form__label">Name for {{ index }}</label>
      <input class="form__input" v-model.trim="person.name" @input="$v.people.$each[index].name.$touch()">
    </div><span class="form-group__message" v-if="!$v.people.$each[index].name.required">Name is required.</span><span class="form-group__message" v-if="!$v.people.$each[index].name.minLength">Name must have at least {{ $v.people.$each[index].name.$params.minLength.min }} letters.</span>
  </div>
  <div>
    <button class="button" @click="people.push({name: ''})">Add</button>
    <button class="button" @click="people.pop()">Remove</button>
  </div>
  <div class="form-group" v-bind:class="{ 'form-group--error': $v.people.$error }"></div><span class="form-group__message" v-if="!$v.people.minLength">List must have at least {{ $v.people.$params.minLength.min }} elements.</span><span class="form-group__message" v-else-if="!$v.people.required">List must not be empty.</span><span class="form-group__message" v-else-if="$v.people.$error">List is invalid.</span>
  <button class="button" @click="$v.people.$touch">$touch</button>
  <button class="button" @click="$v.people.$reset">$reset</button>
  <pre>people: {{ $v.people }}</pre>
</div>

Asynchronous validation

Async support is provided out of the box. Just use a validator that returns a promise. Promise's success value is used for validation directly, failed promise just fails the validation and throws the error.

Any component's data has to be accessed synchronously for correct reactive behaviour. Store it as a variable in validator's scope if you need to use it in any asynchronous callback, for example in .then.

Validator is evaluated on every data change, as it is essentially a computed value. If you need to throttle an async call, do it on your data change event, not on the validator itself. You may end up with broken Vue observables otherwise.

import { required } from 'vuelidate/lib/validators'

export default {
  data () {
    return {
      username: ''
    }
  },
  validations: {
    username: {
      required,
      isUnique (value) {
        // standalone validator ideally should not assume a field is required
        if (value === '') return true

        // simulate async call, fail for all logins with even length
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve(typeof value === 'string' && value.length % 2 !== 0)
          }, 350 + Math.random() * 300)
        })
      }
    }
  }
}

div
  .form-group(v-bind:class="{ 'form-group--error': $v.username.$error, 'form-group--loading': $v.username.$pending }")
    label.form__label Username
    input.form__input(v-model.trim="username" @input="$v.username.$touch()")
  span.form-group__message(v-if="!$v.username.required") Username is required.
  span.form-group__message(v-if="!$v.username.isUnique") This username is already registered.
  pre
    | username: {{ $v.username }}

<div>
  <div class="form-group" v-bind:class="{ 'form-group--error': $v.username.$error, 'form-group--loading': $v.username.$pending }">
    <label class="form__label">Username</label>
    <input class="form__input" v-model.trim="username" @input="$v.username.$touch()">
  </div><span class="form-group__message" v-if="!$v.username.required">Username is required.</span><span class="form-group__message" v-if="!$v.username.isUnique">This username is already registered.</span>
  <pre>username: {{ $v.username }}</pre>
</div>

The async/await syntax is fully supported. It works especially great in combination with Fetch API.

validations: {
  async isUnique (value) {
    if (value === '') return true
    const response = await fetch(`/api/unique/${value}`)
    return Boolean(await response.json())
  }
}

Delayed validation errors

You can do anything you need with the $touch state, no matter how fancy your requirements are. It all boils down to calling $touch and $reset in the right moment. As an example of that, here is an easy to follow implementation of delayed error based on custom setTimeout logic. It triggers one second after last input.

import { required, minLength, maxLength } from 'vuelidate/lib/validators'

const touchMap = new WeakMap()

export default {
  data () {
    return {
      name: ''
    }
  },
  validations: {
    name: {
      required,
      minLength: minLength(4),
      maxLength: maxLength(15)
    }
  },
  methods: {
    delayTouch ($v) {
      $v.$reset()
      if (touchMap.has($v)) {
        clearTimeout(touchMap.get($v))
      }
      touchMap.set($v, setTimeout($v.$touch, 1000))
    }
  }
}

div
  .form-group(v-bind:class="{ 'form-group--error': $v.name.$error }")
    label.form__label Name
    input.form__input(v-model.trim="name" @input="delayTouch($v.name)")
  span.form-group__message(v-if="!$v.name.required") Field is required
  span.form-group__message(v-if="!$v.name.minLength")
    | Name must have at least {{$v.name.$params.minLength.min}} letters.
  span.form-group__message(v-if="!$v.name.maxLength")
    | Name must have at most {{$v.name.$params.maxLength.max}} letters.

  pre
    | name: {{ $v.name }}

<div>
  <div class="form-group" v-bind:class="{ 'form-group--error': $v.name.$error }">
    <label class="form__label">Name</label>
    <input class="form__input" v-model.trim="name" @input="delayTouch($v.name)">
  </div><span class="form-group__message" v-if="!$v.name.required">Field is required</span><span class="form-group__message" v-if="!$v.name.minLength">Name must have at least {{$v.name.$params.minLength.min}} letters.</span><span class="form-group__message" v-if="!$v.name.maxLength">Name must have at most {{$v.name.$params.maxLength.max}} letters.</span>
  <pre>name: {{ $v.name }}</pre>
</div>

Accessing validator parameters

You can access information about your validations through `$params` of a parent validator. This is be useful for reporting errors to users.

import { required, minLength } from 'vuelidate/lib/validators'

export default {
  data () {
    return {
      form: {
        userName: '',
        password: ''
      }
    }
  },
  validations: {
    form: {
      userName: {
        required,
        minLength: minLength(5)
      },
      password: {
        required,
        minLength: minLength(8)
      }
    }
  }
}

div
  .form-group(v-bind:class="{ 'form-group--error': $v.form.userName.$error }")
    label.form__label Nested A
    input.form__input(v-model.trim="form.userName" @input="$v.form.userName.$touch()")
  span.form-group__message(v-if="!$v.form.userName.required")
    | Field is required.
  span.form-group__message(v-if="!$v.form.userName.minLength")
    | Field must have at least {{ $v.form.userName.$params.minLength.min }} characters.
  .form-group(v-bind:class="{ 'form-group--error': $v.form.password.$error }")
    label.form__label Nested B
    input.form__input(v-model.trim="form.password" @input="$v.form.password.$touch()" type="password")
  span.form-group__message(v-if="!$v.form.password.required")
    | Field is required.
  span.form-group__message(v-if="!$v.form.password.minLength")
    | Field must have at least {{ $v.form.password.$params.minLength.min }} characters.

  .form-group(v-bind:class="{ 'form-group--error': $v.form.$error }")
    span.form-group__message(v-if="$v.form.$error") Form is invalid.

  pre form: {{ $v.form }}

<div>
  <div class="form-group" v-bind:class="{ 'form-group--error': $v.form.userName.$error }">
    <label class="form__label">Nested A</label>
    <input class="form__input" v-model.trim="form.userName" @input="$v.form.userName.$touch()">
  </div><span class="form-group__message" v-if="!$v.form.userName.required">Field is required.</span><span class="form-group__message" v-if="!$v.form.userName.minLength">Field must have at least {{ $v.form.userName.$params.minLength.min }} characters.</span>
  <div class="form-group" v-bind:class="{ 'form-group--error': $v.form.password.$error }">
    <label class="form__label">Nested B</label>
    <input class="form__input" v-model.trim="form.password" @input="$v.form.password.$touch()" type="password">
  </div><span class="form-group__message" v-if="!$v.form.password.required">Field is required.</span><span class="form-group__message" v-if="!$v.form.password.minLength">Field must have at least {{ $v.form.password.$params.minLength.min }} characters.</span>
  <div class="form-group" v-bind:class="{ 'form-group--error': $v.form.$error }"><span class="form-group__message" v-if="$v.form.$error">Form is invalid.</span></div>
  <pre>form: {{ $v.form }}</pre>
</div>

Dynamic validation schema

Validations schema can be a function, which will make it dynamic and possibly dependant on your model's data. Recomputations will happend automatically as if it was a computed value. Validation's $dirty state will be preserved as long as the key name won't change or disappear.

import { required } from 'vuelidate/lib/validators'

export default {
  data () {
    return {
      hasDescription: false,
      name: '',
      description: ''
    }
  },
  validations () {
    if (!this.hasDescription) {
      return {
        name: {
          required
        }
      }
    } else {
      return {
        name: {
          required
        },
        description: {
          required
        }
      }
    }
  }
}

div
  .form-group(v-bind:class="{ 'form-group--error': $v.name.$error}")
    label.form__label Name
    input.form__input(v-model.trim="name" @input="$v.name.$touch()")
  .form-group
    label(for="hasDesc").form__label Has description?
    .toggle
      input#hasDesc(type="checkbox", v-model="hasDescription")
      label(for="hasDesc")
        .toggle__switch
  .form-group(v-if="hasDescription" v-bind:class="{ 'form-group--error': $v.description.$error}")
    label.form__label Description
    input.form__input(v-model.trim="description" @input="$v.description.$touch()")
  pre
    | $v: {{ $v }}

<div>
  <div class="form-group" v-bind:class="{ 'form-group--error': $v.name.$error}">
    <label class="form__label">Name</label>
    <input class="form__input" v-model.trim="name" @input="$v.name.$touch()">
  </div>
  <div class="form-group">
    <label class="form__label" for="hasDesc">Has description?</label>
    <div class="toggle">
      <input id="hasDesc" type="checkbox" v-model="hasDescription">
      <label for="hasDesc">
        <div class="toggle__switch"></div>
      </label>
    </div>
  </div>
  <div class="form-group" v-if="hasDescription" v-bind:class="{ 'form-group--error': $v.description.$error}">
    <label class="form__label">Description</label>
    <input class="form__input" v-model.trim="description" @input="$v.description.$touch()">
  </div>
  <pre>$v: {{ $v }}</pre>
</div>

Dynamic parameters

Because the whole validation process is based on computed properties, nothing prevents you from making the validator name dynamic. Such cases allows for very dynamic behaviour even when your data is changing in time.

import { minLength } from 'vuelidate/lib/validators'

export default {
  data () {
    return {
      name: '',
      minLength: 3,
      valName: 'validatorName'
    }
  },
  validations () {
    return {
      name: {
        [this.valName]: minLength(this.minLength)
      }
    }
  }
}

div
  .form-group
    label.form__label Validator name
    input.form__input(v-model.trim="valName" @input="$v.name.$touch()")
  .form-group
    label.form__label Dynamic min length
    input.form__input(type="number" v-model.number="minLength" @input="$v.name.$touch()")
  .form-group(v-bind:class="{ 'form-group--error': $v.name.$error }")
    label.form__label Name
    input.form__input(v-model.trim="name" @input="$v.name.$touch()")
  span.form-group__message(v-if="!$v.name[valName]") Field is invalid
  pre
    | $v: {{ $v }}

<div>
  <div class="form-group">
    <label class="form__label">Validator name</label>
    <input class="form__input" v-model.trim="valName" @input="$v.name.$touch()">
  </div>
  <div class="form-group">
    <label class="form__label">Dynamic min length</label>
    <input class="form__input" type="number" v-model.number="minLength" @input="$v.name.$touch()">
  </div>
  <div class="form-group" v-bind:class="{ 'form-group--error': $v.name.$error }">
    <label class="form__label">Name</label>
    <input class="form__input" v-model.trim="name" @input="$v.name.$touch()">
  </div><span class="form-group__message" v-if="!$v.name[valName]">Field is invalid</span>
  <pre>$v: {{ $v }}</pre>
</div>

API


There are two distinct structures present in vuelidate:

  • validations component option - the definition of your validation
  • $v structure - an object in your viewmodel that holds the validation state

$v values

$v model represents the current state of validation. It does so by defining a set of properties which hold the output of user defined validation functions, following the validations option structure. The presence of those special reserved keywords means that you cannot specify your own validators with that name.

NameTypeDescription
$invalidbooleanIndicates the state of validation for given model. becomes true when any of it's child validators specified in options returns a falsy value. In case of validation groups, all grouped validators are considered.
$dirtybooleanA flag representing if the field under validation was touched by the user at least once. Usually it is used to decide if the message is supposed to be displayed to the end user. Flag is managed manually. You have to use $touch and $reset methods to manipulate it. The $dirty flag is considered true if given model was $touched or all of it's children are $dirty.
$errorbooleanConvenience flag to easily decide if a message should be displayed. It is a shorthand to $invalid && $dirty.
$pendingbooleanIndicates if any child async validator is currently pending. Always false if all validators are synchronous.
$paramsobjectContains types and parameters of all provided validators at the current level, as well as types and parameters of child validation groups, which may be declared using `withParams`. Useful as an input to your error rendering system. Safe to use in translated text.
$eachobjectHolds all validation models of collection validator. Always preserves the keys of original model, so it can be safely referenced in the v-for loop iterating over your data using the same index.

$v methods

A set of methods to control the validation model. Accessible on every level of nesting. All methods are ment to be used on any event handler you wish. There is no extra syntax to decide when the dirty flag should be set. Just use standard @input or @blur bindings.

NameDescription
$touchSets the $dirty flag of the model and all its children to true recursively.
$resetSets the $dirty flag of the model and all its children to false recursively.
$flattenParamsReturns an array of leaf params.

Config keywords

NameTypeDescription
$eachobjectA definition of nested validation applied to each prop of given model separately. Perfect for validation arrays, but can be used with any object or collection.
$trackBystring || funcMust be a direct children of $each, but is optional. Defines the accessor to object's property by which $each tracks it's child models. Necessary to correctly preserve $dirty flag on random insertions. If this property not preset, the key is used for tracking.

Validators


vuelidate comes with a set of builtin validators that you can just require and use, but it doesn't end there. All of those are just simple predicates - functions of data into boolean, which denotes if data is valid. You can easily write your own or use any function in this shape from any library you already have, like _.conforomsTo from lodash or higher order functions and chains like R.cond from ramda. Think of the possibilities.

This documentation presents every builtin validator with short description and presents an example custom validator implementation to help understanding them and writing your own as easy as possible.

Builtin validators

To use any of builtin validators, you have to import it from vuelidate library.

import { required, maxLength } from 'vuelidate/lib/validators'

You can also import specific validators directly, to avoid loading unused ones in case your bundler doesn't support tree shaking. This is not required for Rollup or Webpack 2 among others.

import required from 'vuelidate/lib/validators/required'
import maxLength from 'vuelidate/lib/validators/maxLength'

It is possible to use validators directly in browser by using a browser-ready bundle. Keep in mind this will always load all builtin validators at once.

<script src="vuelidate/dist/validators.min.js"></script>
var required = validators.required
var maxLength = validators.maxLength

Here is a full list of provided validators.

NameMeta parametersDescription
requirednoneRequires non-empty data. Checks for empty arrays and strings containing only whitespaces.
requiredIflocatorRequires non-empty data only if provided property or predicate is true.
requiredUnlesslocatorRequires non-empty data only if provided property or predicate is false.
minLengthmin lengthRequires the input to have a minimum specified length, inclusive. Works with arrays.
maxLengthmax lengthRequires the input to have a maximum specified length, inclusive. Works with arrays.
betweenmin, maxChecks if a number is in specified bounds. Min and max are both inclusive.
alphanoneAccepts only alphabet characters.
alphaNumnoneAccepts only alphanumerics.
numericnoneAccepts only numerics.
emailnoneAccepts valid email addresses. Keep in mind you still have to carefully verify it on your server, as it is impossible to tell if the address is real without sending verification email.
sameAslocatorChecks for equality with a given property. Locator might be either a sibling property name or a function, that will get your component as this and nested model which sibling properties under second parameter.
urlnoneAccepts only URLs.
orvalidators...Passes when at least one of provided validators passes.
andvalidators...Passes when all of provided validators passes.
withParams$params, validatorNot really a validator, but a validator modifier. Adds a `$params` object to the provided validator. Can be used on validation functions or even entire nested field validation objects. Useful for creating your own custom validators.

Validator parameters

Every validator can save parameters. Validators are responsible for saving their type and parameters, because they are simple functions, and we may want to inform the user about them.

Use withParams to apply parameters to a validator. Declared parameters bubble up by one level, so they are included in the $params of the parent validation object. Vuelidate is designed in a way that does not allow the validation result to directly include params.

You may call the $flattenParams method on a validation object to get an array of validator params for all validators that exist in that validation object. For example, let's say a validation object contains a between validator to check that a value is between 5 and 10. Calling $flattenParams returns the following array.

[{ path: [], name: 'between', params: { type: 'between', min: 5, max: 10 } }]

Custom validators

You can easily write custom validators and combine them with builtin ones, as those are just a simple predicate functions.

Let's take a simple required as an example. It uses `withParams` to save its own type so that it can be later accessed. Besides the use of `withParams`, it's simply just a function that, given it's input value, returns if the data is valid or not.

import {withParams, req} from './common'
export default withParams({type: 'required'}, req)

If your validator needs extra parameters, you can simply create a higher order function that returns the actual predicate, like in between.

import {req, withParams} from './common'

export default (min, max) =>
  withParams({type: 'between', min, max}, value =>
    !req(value) || (!/\s/.test(value) && +min <= +value && +max >= +value))

In more complex cases when access to the whole model is necessary, like sameAs, make use of the function context (this) to access any value on your component or use provided parentVm to access sibling properties.

import {ref, withParams} from './common'
export default equalTo => withParams(
  {type: 'sameAs', eq: equalTo},
  function (value, parentVm) {
    return value === ref(equalTo, this, parentVm)
  }
)

Created by Damian Dulisz & PaweĊ‚ Grabarz

With love from Monterail