VeeValidate Logo

Examples

Usage and Examples

Delayed Validation (Debounced)

You can specify a delay to debounce the input event, a case scenario that you may want to wait for the user to stop typing then validate the field. This can be achieved by adding a data-vv-delay attribute on the field being validated, and assign it the number of milliseconds you want to wait for.

export default { name: 'delay-example' };
<div class="columns is-multiline"> <div class="column is-12"> <label class="label">Email (1s delay)</label> <p class="control has-icon has-icon-right"> <input name="email" v-validate="'required|email'" data-vv-delay="1000" :class="{'input': true, 'is-danger': errors.has('email') }" type="text" placeholder="Email"> <i v-show="errors.has('email')" class="fa fa-warning"></i> <span v-show="errors.has('email')" class="help is-danger">{{"{" + "{ errors.first('email') }" + "}"}}</span> </p> </div> <div class="column is-12"> <label class="label">Name (0.5s delay)</label> <p class="control has-icon has-icon-right"> <input name="name" v-validate="'required|alpha'" data-vv-delay="500" :class="{'input': true, 'is-danger': errors.has('name') }" type="text" placeholder="Name"> <i v-show="errors.has('name')" class="fa fa-warning"></i> <span v-show="errors.has('name')" class="help is-danger">{{"{" + "{ errors.first('name') }" + "}"}}</span> </p> </div> </div>

Validate Models

The v-validate directive detects if the input has v-model bound to the same input, and watches for that value and validates it when it changes. But it can also listen for changes in computed properties by using the arg like this:

<input v-validate:name="'required|alpha_spaces'" type="text" name="name">
export default {
  data: () => ({
    first_name: '',
    last_name: ''
  }),
  computed: {
    name() {
      return this.first_name + ' ' + this.last_name;
    }
  }
};

The expression for both the arg and the v-model directive must be a simple dot notation expression, and that expression must exist on the instance. For example, having inputs in a loop bound by the iterator won't work properly and will instead use the traditional listeners.

As you can see, the arg is name which tells v-validate to watch for whenever the name changes, this example might be a little bit silly, but you might need this functionality.

You can debounce the validation using the lazy modifier on your v-model directive which will cause the validation to occur only when the user leaves the input.

Initial Value Validation

In the next example, notice the usage of .initial modifier to force the validation of the field initial value.

export default { name: 'data-example', data: () => ({ email: '', first_name: '', last_name: '' }), computed: { name() { return `${this.first_name} ${this.last_name}`; } } };
<div class="columns is-multiline"> <div class="column is-12"> <label class="label">Email</label> <p class="control has-icon has-icon-right"> <input name="email" v-model="email" v-validate.initial="'required|email'" :class="{'input': true, 'is-danger': errors.has('email') }" type="text" placeholder="Email"> <i v-show="errors.has('email')" class="fa fa-warning"></i> <span v-show="errors.has('email')" class="help is-danger">{{"{" + "{ errors.first('email') }" + "}"}}</span> </p> </div> <div class="column is-12"> <label class="label">First Name</label> <p class="control has-icon has-icon-right"> <input name="first_name" v-model="first_name" v-validate.initial="'required|alpha'" :class="{'input': true, 'is-danger': errors.has('first_name') }" type="text" placeholder="First Name"> <i v-show="errors.has('first_name')" class="fa fa-warning"></i> <span v-show="errors.has('first_name')" class="help is-danger">{{"{" + "{ errors.first('first_name') }" + "}"}}</span> </p> </div> <div class="column is-12"> <label class="label">Last Name</label> <p class="control has-icon has-icon-right"> <input name="last_name" v-model="last_name" v-validate.initial="'required|alpha'" :class="{'input': true, 'is-danger': errors.has('last_name') }" type="text" placeholder="Last Name"> <i v-show="errors.has('last_name')" class="fa fa-warning"></i> <span v-show="errors.has('last_name')" class="help is-danger">{{"{" + "{ errors.first('last_name') }" + "}"}}</span> </p> </div> <div class="column is-12"> <label class="label">Full Name</label> <p class="control has-icon has-icon-right"> <input :value="name" name="name" v-validate:name.initial="'required|alpha_spaces'" :class="{'input': true, 'is-danger': errors.has('name') }" type="text" placeholder="Full Name"> <i v-show="errors.has('name')" class="fa fa-warning"></i> <span v-show="errors.has('name')" class="help is-danger">{{"{" + "{ errors.first('name') }" + "}"}}</span> </p> </div> </div>

Validate Form Before Submit

You may want to trigger all inputs validation before submitting a form, maybe display an alert or prevent form submission if any errors are detected. This can be easily achieved using validateAll method.

export default { name: 'form-example', data: () => ({ email: '', name: '', phone: '', url: '' }), methods: { validateBeforeSubmit() { this.$validator.validateAll().then((result) => { if (result) { // eslint-disable-next-line alert('From Submitted!'); return; } alert('Correct them errors!'); }); } } };
<form @submit.prevent="validateBeforeSubmit"> <div class="column is-12"> <label class="label">Email</label> <p class="control has-icon has-icon-right"> <input name="email" v-model="email" v-validate="'required|email'" :class="{'input': true, 'is-danger': errors.has('email') }" type="text" placeholder="Email"> <i v-show="errors.has('email')" class="fa fa-warning"></i> <span v-show="errors.has('email')" class="help is-danger">{{"{" + "{ errors.first('email') }" + "}"}}</span> </p> </div> <div class="column is-12"> <label class="label">Name</label> <p class="control has-icon has-icon-right"> <input name="name" v-model="name" v-validate="'required|alpha'" :class="{'input': true, 'is-danger': errors.has('name') }" type="text" placeholder="Name"> <i v-show="errors.has('name')" class="fa fa-warning"></i> <span v-show="errors.has('name')" class="help is-danger">{{"{" + "{ errors.first('name') }" + "}"}}</span> </p> </div> <div class="column is-12"> <label class="label">Phone</label> <p class="control has-icon has-icon-right"> <input name="phone" v-model="phone" v-validate="'required|numeric'" :class="{'input': true, 'is-danger': errors.has('phone') }" type="text" placeholder="Phone"> <i v-show="errors.has('phone')" class="fa fa-warning"></i> <span v-show="errors.has('phone')" class="help is-danger">{{"{" + "{ errors.first('phone') }" + "}"}}</span> </p> </div> <div class="column is-12"> <label class="label">Website</label> <p class="control has-icon has-icon-right"> <input name="url" v-model="url" v-validate="'required|url'" :class="{'input': true, 'is-danger': errors.has('url') }" type="text" placeholder="Website"> <i v-show="errors.has('url')" class="fa fa-warning"></i> <span v-show="errors.has('url')" class="help is-danger">{{"{" + "{ errors.first('url') }" + "}"}}</span> </p> </div> <div class="column is-12"> <p class="control"> <button class="button is-primary" type="submit">Submit</button> </p> </div> </form>

Localized Messages

You may want to display error messages in different languages, vee-validate offers a lot of flexibility for multi-language, here is an example on how you may do that

The language below is Arabic (RTL):

import arabic from 'vee-validate/dist/locale/ar'; export default { name: 'locale-example', data: () => ({ email: '', phone: '', locale: 'en', }), computed: { nextLocale() { return this.locale === 'en' ? 'Arabic' : 'English'; } }, methods: { changeLocale() { this.locale = this.$validator.locale === 'ar' ? 'en' : 'ar'; this.$validator.localize(this.locale); } }, created() { this.$validator.localize('ar', { messages: arabic.messages, attributes: { email: 'البريد الاليكتروني', phone: 'رقم الهاتف' } }); // start with english locale. this.$validator.localize('en'); } };
<div class="columns is-rtl is-multiline"> <button @click="changeLocale" type="button" class="button is-primary">Change Locale To {{"{" + "{ nextLocale }" + "}"}}</button> <div class="column is-12"> <label class="label">البريد الاليكتروني (Email)</label> <p class="control has-icon has-icon-left"> <input name="email" v-validate="'required|email'" :class="{'input': true, 'is-danger': errors.has('email') }" type="text" placeholder="Email"> <i v-show="errors.has('email')" class="fa fa-warning"></i> <span v-show="errors.has('email')" class="help is-danger has-text-right">{{"{" + "{ errors.first('email') }" + "}"}}</span> </p> </div> <div class="column is-12"> <label class="label">رقم الهاتف (Phone)</label> <p class="control has-icon has-icon-left"> <input name="phone" v-validate="'required|numeric'" :class="{'input': true, 'is-danger': errors.has('phone') }" type="text" placeholder="Phone"> <i v-show="errors.has('phone')" class="fa fa-warning"></i> <span v-show="errors.has('phone')" class="help is-danger has-text-right">{{"{" + "{ errors.first('phone') }" + "}"}}</span> </p> </div> </div>

Scopes

By default, the scope of the validator is the same as the Vue instance that owns it. Sometimes you may have multiple fields within the same component, they are in different forms and serve different purposes. The validator will then treat those two fields as the same field which will cause problems detecting the input and displaying the errors.

You can tell the validator to scope the fields by adding a data-vv-scope attribute which tells the validator the name of the scope. Those fields will be then identified using their name and their scope, you can have inputs with the same name in different scopes, and you can display, clear and validate those scopes independently.

For convenience, you may add the data-vv-scope attribute on the form that owns the inputs, you don't have to add the attribute on every input. You can also pass scope property to the validator expression.

export default { name: 'scopes-example', methods: { validateForm(scope) { this.$validator.validateAll(scope).then((result) => { if (result) { // eslint-disable-next-line alert('Form Submitted!'); } }); } } };
<div class="columns is-multiline"> <form @submit.prevent="validateForm('form-1')" class="columns column is-multiline is-12" data-vv-scope="form-1"> <legend>Form 1</legend> <div class="column is-12"> <label class="label">Email</label> <p class="control has-icon has-icon-right"> <input name="email" v-validate="'required|email'" :class="{'input': true, 'is-danger': errors.has('form-1.email') }" type="text" placeholder="Email"> <i v-show="errors.has('form-1.email')" class="fa fa-warning"></i> <span v-show="errors.has('form-1.email')" class="help is-danger">{{"{" + "{ errors.first('form-1.email') }" + "}"}}</span> </p> </div> <div class="column is-12"> <label class="label">Password</label> <p class="control has-icon has-icon-right"> <input name="password" v-validate="'required|min:6'" :class="{'input': true, 'is-danger': errors.has('form-1.password') }" type="password" placeholder="Password"> <i v-show="errors.has('form-1.password')" class="fa fa-warning"></i> <span v-show="errors.has('form-1.password')" class="help is-danger">{{"{" + "{ errors.first('form-1.password') }" + "}"}}</span> </p> </div> <div class="column is-12"> <p class="control"> <button class="button is-primary" type="submit" name="button">Sign up</button> <button class="button is-danger" type="button" name="button" @click="errors.clear('form-1')">Clear</button> </p> </div> </form> <form @submit.prevent="validateForm('form-2')" class="columns column is-multiline is-12" data-vv-scope="form-2"> <legend>Form 2</legend> <div class="column is-12"> <label class="label">Email</label> <p class="control has-icon has-icon-right"> <input name="email" v-validate="'required|email'" :class="{'input': true, 'is-danger': errors.has('form-2.email') }" type="text" placeholder="Email"> <i v-show="errors.has('form-2.email')" class="fa fa-warning"></i> <span v-show="errors.has('form-2.email')" class="help is-danger">{{"{" + "{ errors.first('form-2.email') }" + "}"}}</span> </p> </div> <div class="column is-12"> <label class="label">Password</label> <p class="control has-icon has-icon-right"> <input name="password" v-validate="'required|min:6'" :class="{'input': true, 'is-danger': errors.has('form-2.password') }" type="password" placeholder="Password"> <i v-show="errors.has('form-2.password')" class="fa fa-warning"></i> <span v-show="errors.has('form-2.password')" class="help is-danger">{{"{" + "{ errors.first('form-2.password') }" + "}"}}</span> </p> </div> <div class="column is-12"> <p class="control"> <button class="button is-primary" type="submit" name="button">Sign up</button> <button class="button is-danger" type="button" name="button" @click="errors.clear('form-2')">Clear</button> </p> </div> </form> </div>

Async Validation

Let's say you want to validate something specific to your app domain that isn't provided by the default validators. For example, lets validate a user coupon on checkout. If it is a valid coupon then you discount it for him, if not he pays the full price :(

Here is a list of our valid coupons: SUMMER2016, WINTER2016 and FALL2016.

Each of which gives 20% off. The process of validation is as follows: we take the input and send it to backend, the response should determine if the coupon is valid which is up to you. Here I'm simulating async behavior using setTimeout.

Vee-Validate allows the usage of async validators, but it requires them to return a promise that resolves with an object containing the property valid which should equal a boolean state of the validation status.

import { Validator } from 'vee-validate'; export default { name: 'coupon-example', data: () => ({ coupon: '', price: 100, discounted: false }), computed: { discountedPrice() { return this.discounted ? this.price - (0.2 * this.price) : this.price; } }, methods: { applyCoupon() { this.$validator.validate('coupon', this.coupon).then((result) => { this.discounted = result; }); } }, created() { Validator.extend('verify_coupon', { getMessage: field => `The ${field} is not a valid coupon.`, validate: value => new Promise((resolve) => { // API call or database access. const validCoupons = ['SUMMER2016', 'WINTER2016', 'FALL2016']; setTimeout(() => { resolve({ valid: value && validCoupons.indexOf(value.toUpperCase()) !== -1 }); }, 500); }) }); this.$validator.attach({ name: 'coupon', rules: 'required|verify_coupon' }); } };
<div class="columns is-multiline"> <div class="column is-12"> <span :class="{ 'discounted': discounted }">Price: {{"{" + "{ price }" + "}"}}$</span> <span v-show="discounted" class="SeemsGood">{{"{" + "{ discountedPrice }" + "}"}}$</span> </div> <form @submit.prevent="applyCoupon" class="column is-12"> <label class="label">Coupon</label> <p class="control has-icon has-icon-right"> <input v-model="coupon" name="coupon" :class="{'input': true, 'is-danger': errors.has('coupon') }" type="text" placeholder="Enter Your Coupon"> <i v-show="errors.has('coupon')" class="fa fa-warning"></i> <span v-show="errors.has('coupon')" class="help is-danger">{{"{" + "{ errors.first('coupon') }" + "}"}}</span> </p> <p class="control"> <button type="submit" class="button is-primary" name="button">Apply</button> </p> </form> </div>

Radio Buttons

vee-validate also supports validating radio buttons, you can use whatever rules you want on them but only few rules make sense, like required. One thing to note in this example is that you only need to use the directive on one of the radio buttons, you don't need to attach it on every one, they all must share the same name though.

In the following example, the third value is not included using the rule in:1,2

export default { name: 'radio-buttons-example', data: () => ({ radio_group_2: '', }), methods: { validateForm() { this.$validator.validateAll().then((result) => { if (result) { // eslint-disable-next-line alert('All Passes!'); return; } alert('Oh NO!'); }); } } };
<form @submit.prevent="validateForm"> <div class="columns is-multiline"> <div class="column is-6"> <legend :class="{ 'error': errors.has('radio_group_1') }">Radio Group 1</legend> <p class="control"> <label class="radio"> <input name="radio_group_1" v-validate="'required|in:1,2'" value="1" type="radio"> Option 1 </label> <label class="radio"> <input name="radio_group_1" value="2" type="radio"> Option 2 </label> <label class="radio"> <input name="radio_group_1" value="3" type="radio"> Option 3 </label> </p> <span class="help is-danger" v-show="errors.has('radio_group_1')">{{"{" + "{ errors.first('radio_group_1') }" + "}"}}</span> </div> <div class="column is-6"> <legend :class="{ 'error': errors.has('radio_group_2') }">Radio Group 2 (Bound)</legend> <p class="control"> <label class="radio"> <input v-model="radio_group_2" v-validate="{ rules: 'required|in:1,2', arg: 'radio_group_2' }" name="radio_group_2" value="1" type="radio"> Option 1 </label> <label class="radio"> <input v-model="radio_group_2" name="radio_group_2" value="2" type="radio"> Option 2 </label> <label class="radio"> <input v-model="radio_group_2" name="radio_group_2" value="3" type="radio"> Option 3 </label> </p> <span class="help is-danger" v-show="errors.has('radio_group_2')">{{"{" + "{ errors.first('radio_group_2') }" + "}"}}</span> </div> </div> <p class="control"> <button type="submit" class="button is-primary" name="button">Apply</button> </p> </form>

Checkbox Example

vee-validate also supports validating checkboxes. However, like radio buttons, the extent of the support is limited by the input nature itself, but all rules work regardless. Like for radio buttons, you only have to attach the validator directive and attributes on the checkbox under validation. If there are multiple checkboxes (group), you only have to add the directive on one of them.

If multiple values are selected, the validator will apply the validations on each checkbox.

In the following example, the most basic use of checkboxes validation is the terms and conditions agreement that nobody reads.

const terms = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit '; export default { name: 'checkbox-example', data: () => ({ terms: terms.repeat(20) }), methods: { nextStep() { this.$validator.validateAll().then((result) => { if (result) { alert('You just agreed to conditions without reading it.'); return; } // eslint-disable-next-line alert('You actually did not agree?'); }); } } };
<div> <div class="terms"> <p> {{"{" + "{ terms }" + "}"}} </p> </div> <div> <p class="control"> <label class="checkbox"> <input name="terms" v-validate="'required'" type="checkbox"> I agree to the terms and conditions. </label> <span class="help is-danger" v-show="errors.has('terms')">{{"{" + "{ errors.first('terms') }" + "}"}}</span> </p> </div> <p class="control"> <button type="button" class="button is-primary" @click="nextStep">Next</button> </p> </div>

Error Selectors

The errors.first and errors.has methods don't only provide you with a way to fetch the first input for a specific field, they also allow you to filter it down further to a specific rule, using the following syntax 'field:rule', even more, they allow you to filter it down to a specific scope using 'scope.field', so if you want to display the first error for the email field in the newsletter form but only if the rule is email

errors.first('newsletter.email:email');

In the example below, you have a collection of errors and you may use the input to filter down the errors. Note that it is currently unlikely that the error bag will have multiple errors of the same input since it exits early upon first failure.

export default { name: 'selectors-example', data: () => ({ selector: '' }), computed: { selectedError() { if (! this.selector) { return 'You did not select any error'; } return this.errors.first(this.selector) || 'None Found'; } }, created() { this.errors.add('email', 'Newsletter Email is not valid', 'email', 'newsletter'); this.errors.add('email', 'Newsletter Email is required', 'required', 'newsletter'); this.errors.add('email', 'Email is not a valid email', 'email'); this.errors.add('name', 'name is required', 'required'); } };
<div class="columns is-multiline"> <div class="column is-12"> <label class="label">Type the selector</label> <p class="control"> <input v-model="selector" class="input" type="text" placeholder="Rule Name"> </p> </div> <div class="column is-12"> <label><b>Selected Error</b></label> <p> {{"{" + "{ selectedError }" + "}"}} </p> </div> <div class="column is-12"> <label><b>Available Errors:</b></label> <pre>{{"{" + "{ errors }" + "}"}}</pre> </div> </div>

Custom Component Validation

You might have a custom component that you want to treat as an input. Like a custom input field, it would have its own validator instance, but you want to validate it in the parent scope because it is simply just an input with some whistles on top. You can achieve this by using the directive normally like you would on a regular input element, but you must make sure that your component satisfies the following:

  • Must emit an input event whenever the value changes.
  • Should have a data-vv-name or a name attribute defined.
  • Should have a data-vv-value-path attribute which denotes how to access the value from within that component (Needed for validateAll calls).
import CustomInput from '../CustomInput.vue'; export default { name: 'component-example', components: { CustomInput }, methods: { validate() { this.$validator.validateAll().then((result) => { // eslint-disable-next-line alert(`Validation Result: ${result}`); }); } } };
<div class="columns is-multiline"> <div class="column is-12"> <custom-input v-validate="'required|email'" data-vv-value-path="innerValue" data-vv-name="custom" label="Email" :has-error="errors.has('custom')"> </custom-input> <span v-show="errors.has('custom')" class="help is-danger">{{"{" + "{ errors.first('custom') }" + "}"}}</span> <button @click="validate" type="button" class="button is-primary">Validate All</button> </div> </div>