Installation and usage of vee-validate

Posted on Mar 27, 2021

This week I had to find a way to properly validate the 48 forms present on the website I’m working on.

In vue 2 there are three main ways to validate forms :

I experienced the three and finally opted to use the first option, the vee-validate framework.

Upon testing, I found that vuelidate is lacking critical features and is overall less convenient to use than vee-validate.
I tested them for a time far too short, and my opinion may be incorrect, but I found people online with the same opinion as me.
All while googling problems I experienced with the basic usage of vuelidate.

The ‘all by hand’ option took more time to set aside, I really wanted the flexibility it offered but, I finally opted for the fastest option.

Now I needed a clean setup, the documentation is complete but also scattered over several pages. This tutorial is made in an effort to combine this scattered knowledge.

This tutorial is made for a usage with vue 2

Installation

Advanced instructions are available here

I used NPM to install the package, it’s the only thing you’ll need to install :

npm install vee-validate --save

Global setup

Next is the code you’ll need to load the package into vuejs :

App.js (could be named something else)

import { ValidationProvider } from 'vee-validate'
import { ValidationObserver } from 'vee-validate'
import { extend } from 'vee-validate'
import * as rules from 'vee-validate/dist/rules'
import { localize } from 'vee-validate'
import fr from 'vee-validate/dist/locale/fr.json'

Vue.component('ValidationProvider', ValidationProvider)
Vue.component('ValidationObserver', ValidationObserver)
Object.keys(rules).forEach(rule => {
    extend(rule, rules[rule])
})

localize('fr', fr)
require('./dictionaries/fr.js')

Many things are involved here, you’ll want this setup most of the time.

For the imports the ValidationProvider and ValidationObserver will be used to surround your forms and input fields in order to validate them.

Next we have the rules imports, the ‘extend’ is used to make your own rules, you won’t need this often.
The ‘* as rules’ import is used to import all the default rules.
It’s advised not to load all the rules globally but to load them locally in the components where you need them, I personally don’t want to bother with imports everywhere.

Next are the localization imports, those are important if you’re developing a website for a non-english country.
In my case it’ll be in French.
The { localize } import allow to replace the default error strings with the equivalents in the locale of your choice. Then I import the default strings in the French locale.

Everything else is easy to figure out, I load the components, the rules, the locale.

The last line is of use if you want pretty error messages :
By default vee-validate will display errors like so : “The field user_lastname is invalid”.
That’s not pretty, you’ll want a dictionary in which vee-validate can find the key user_lastname and replace it with a prettier alternative.

Here’s the content of my dictionary (just started and for a French app) :

import {localize} from "vee-validate";

localize({
    fr: {
        names: {
            email: 'e-mail',
            password: 'mot de passe',
            city: 'ville',
            phone_number: 'numéro de téléphone'
        }
    },
})

Local usage

Now for the usage in your components we can picture something like so :

<ValidationObserver ref="form">
    <form class="text-center" @submit.prevent>
        <ValidationProvider name="email" rules="required|email">
            <input id="email" v-model="learner.email" type="email" name="email" placeholder="E-mail">
        </ValidationProvider>
        <a href="#" class="btn btn-primary" @click.prevent="postLearner(true)">
            Save the learner
        </a>
    </form>
</ValidationObserver>

Compared to a classic form there is some decoration added here.
The ValidationObserver always surround your form, give him a unique ref to make him selectable in your component methods.
The ValidationProvider surround the fields you want to validate, the rules to check are provided through the ‘rules’ prop.

I like to prevent the submission of the form to make sure the page will never be reloaded but that’s not necessary.

The next important part happens in the methods of the component, upon simulated submission of the form :

postLearner: async function (edit) {
    this.$refs.form.validateWithInfo().then(async (result) =>  {
        if (result.isValid) {
            // send your valid form 
        } else {
            let errors_decription = ""

            for (const [key, value] of Object.entries(result.errors)) {
                if(value.length) {
                    errors_decription += value[0] + '<br>'
                }
            }

            this.$notify({
                group: 'main-notifications',
                title: 'Erreur dans le formulaire',
                text: 'Des champs ont été incorectement remplis :<br>' + errors_decription,
                type: 'error',
            });
        }
    })
}

Upon submission, you have access to your validationObserver through the previously chosen ref (here the unoriginal ‘form’ -> this.$refs.form). A full api is now provided, but you’ll want to validateWithInfo(), then gather the result, check if it’s valid and act accordingly. Send your form as usual if ok or tell your users what’s wrong if not.

And that’s it !

Additional tips

Sometimes you’ll want to use complex rules like regex to validate zip-codes or phone numbers, a small tweak in the syntax is necessary to make sure everything is parsed correctly :

<ValidationProvider name="phone_number" :rules="{ required: true, regex:/^(?:(?:\+|00)33|0)\s*[1-9](?:[\s.-]*\d{2}){4}$/ }">
    <input id="phone_number" v-model="learner.phone_number" type="text" name="phone_number" placeholder="Téléphone">
</ValidationProvider>

Use :rules instead of rule and pass an object (this syntax can be used everywhere).