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 acheived 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 indepently.

For convienece 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('coupon', '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 makes 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 the radio buttons you only have 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>

Flags

vee-validate includes few flags that could help you improve your user experience, each field under validation has its own set of flags which are:

  • touched: indicates that the field has been touched or focused.
  • untouched: indicates that the field has not been touched nor focused.
  • dirty: indicates that the field has been manipluated.
  • pristine: indicates that the field has not been manipluated.
  • valid: indicates that the field has been validated at least once and that it passed the validation.
  • invalid: indicates that the field has been validated at least once and that it failed the validation.

The flags are reactive objects, so you can build computed properties based on them. For example here is how you can tell if a form has been manipulated, say maybe to disable/enable a button.

export default {
  // ...
  computed: {
    isFormDirty() {
      return Object.keys(this.fields).some(key => this.fields[key].dirty);
    }
  },
  //...
}

The global fields flags are accessed via objects like this:

// Is the 'name' field dirty? 
this.fields.name.dirty;

However, for the scoped fields the FieldBag will group those fields in an property name that is prefixed by a $ to indicate that it is a scope object:

// Is the 'name' field dirty? 
this.fields.$myScope.name.dirty;

// Is the 'name' field clean? 
this.fields.$myScope.name.pristine; 

Here is what it would look like:

<div class="form-input">
  <input type="text" name="email" v-validate="'required|email'" placeholder="Email">
  <span v-show="errors.has('field')">{{ errors.first('field') }}</span>
  <span v-show="fields.email && fields.email.dirty">I'm Dirty</span>
  <span v-show="fields.email && fields.email.touched">I'm touched</span>
  <span v-show="fields.email && fields.email.valid">I'm valid</span>
</div>
<div class="form-input">
  <input data-vv-scope="scope" type="text" name="email" v-validate="'required|email'" placeholder="Email">
  <span v-show="errors.has('scope.field')">{{ errors.first('scope.field') }}</span>
  <span v-show="fields.$scope && fields.$scope.email && fields.$scope.email.dirty">I'm Dirty</span>
</div>

Notice the additional checks before the actual flag check, this is because the flags aren't actually available until the mounted() life cycle event, so to avoid created() life cycle errors we need to add those checks.

This can become quite tedious if you are referencing multiple flags, so It might be useful to use the mapFields helper, which is similair to Vuex's mapGetters and mapActions as it maps a field object to a computed property.

import { mapFields } from 'vee-validate'

export default {
  // ...
  computed: mapFields(['name', 'email', 'scope.email']),
 // ...
}

You can also provide an object to rename the mapped props:

import { mapFields } from 'vee-validate'

export default {
  // ...
  computed: mapFields({
    fullname: 'name',
    phone: 'scope.phone'
  }),
 // ...
}

Note that scoped fields names in the array from is mapped to a non-nested name. and you can use the object spread operator to add the mapped fields to your existing computed components:

import { mapFields } from 'vee-validate'

export default {
  // ...
  computed: {
    ...mapFields(['name', 'email', 'scope.phone']),
    myProp() {
       // ....
    }
  },
 // ...
}

Additionally, In case you want to set the flags manually, you can use the Validator.flag(fieldName, flagsObj) method:

// flag the field as valid and dirty.
this.$validator.flag('field', {
  valid: false,
  dirty: true
});

// set flags for scoped field.
this.$validator.flag('scoped.field', {
  touched: false,
  dirty: false
});

For custom components, in order for the flags to fully work reliably, you need to emit those events:

The input event, which you probably already emit, will set the dirty and pristine flags.

this.$emit('input', value); 

// The focus event which will set the touched and untouched flags.
this.$emit('focus'); 

Here is an example that displays those flags, intereact with the input and watch the flags change accordingly:

import { mapFields } from 'vee-validate'; export default { name: 'flags-example', data: () => ({ email: '' }), computed: { ...mapFields({ nameFlags: 'name' }) } };
<div class="columns is-multiline"> <div class="column is-12"> <label class="label">Name</label> <p class="control has-icon has-icon-right"> <input name="name" v-validate="'required|alpha'" :class="{'input': true }" type="text" placeholder="Name"> </p> <pre>{{"{" + "{ nameFlags }" + "}"}}</pre> </div> </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 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 currently it is unlikely that the error bag will have multiple errors of the same input since it early exit 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 it 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>

Event Bus

There are situations where you need to 'transfer' or communicate errors from child components to their parent and vice versa. Although this is not related to the plugin, the need is common enough to require an example.

This example uses Vue.js event bus to communicate errors with the parent component, the parent component can also clear, or force validate the child component. Original example was created by @sproogen.

There is a better approach introduced by using the Provide/Inject API.

import bus from './bus'; import ChildComponent from './EventBusChild.vue'; export default { name: 'event-bus-example', components: { ChildComponent }, methods: { validateChild() { bus.$emit('validate'); }, clearChild() { bus.$emit('clear'); } }, created() { bus.$on('errors-changed', (errors) => { this.errors.clear(); errors.forEach((e) => { this.errors.add(e.field, e.msg, e.rule, e.scope); }); }); } };
<div class="columns is-multiline"> <child-component></child-component> <div class="column is-12"> <button class="button is-info" type="button" @click="validateChild">Validate Child</button> <button class="button is-danger" type="button" @click="clearChild">Clear Child Errors</button> </div> </div>

Backend Validation

You might need to also use your Laravel/Express or whatever backend as your validation provider for numerous reasons, like checking if an email is unique since it is hard to implement on the client-side, we can acheive this using a custom rule and the reasoning feature:

import axios from 'axios'; // great ajax library.
import { Validator } from 'vee-validate';

const isUnique = (value) => {
  return axios.post('/api/validate/email', { email: value }).then((response) => {
    // Notice that we return an object containing both a valid property and a data property.
    return {
      valid: response.data.valid,
      data: {
        message: response.data.message
      }
    };
  });
};

// The messages getter may also accept a third parameter that includes the data we returned earlier.
Validator.extend('unique', {
  validate: isUnique,
  getMessage: (field, params, data) => {
    return data.message;
  }
});

The following demo shows how would it work in action, note that it will only trigger if the user entered a valid email since the validator early exits upon first failure.

Since there is no real DB in this example, Its being simulated by a dynamic array.

import { Validator } from 'vee-validate'; export default { name: 'backend-example', data: () => ({ // simulate emails database emailsDB: [], email: null }), created() { // simulated DB. const isUnique = value => new Promise((resolve) => { setTimeout(() => { if (this.emailsDB.indexOf(value) === -1) { return resolve({ valid: true }); } return resolve({ valid: false, data: { message: `${value} is already taken.` } }); }, 200); }); Validator.extend('unique', { validate: isUnique, getMessage: (field, params, data) => data.message }); }, methods: { submit() { this.emailsDB.push(this.email); this.email = ''; this.$nextTick().then(() => { this.$validator.reset(); }); } } };
<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="'required|email|unique'" :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"> <p class="control"> <button class="button is-primary" type="button" @click="submit">Submit</button> </p> </div> </div>