fbpx

Vue JS form validation using Vuelidate library

Reading time: 4 minutes

Problem

While using Vue JS we might need to validate the form with model-based validation strategy.

Final output

At the end of the article we will be able to do Simple, lightweight model-based validation by using Vuelidate Vue Js library.

Vuelidate final output
Vue Js Vuelidate plugin final output

Vuelidate Features & characteristics?
Model based
Decoupled from templates
Dependency free, minimalistic library
Support for collection validations
Support for nested models
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.

There is a quick example of how to setup and use form validation in Vue JS with Vuelidate library. Most of the time form validation has a reputation for being tricky to implement.

So, let’s start to achieve our goal step by step 🙂

Solution

1. Styling of the Vuelidate example is all done with Bootstrap 4 CSS. So, let’s add bootstrap 4 in our project.
Go to public folder then inside index.html add CDN’s.
To get the latest version of Bootstrap 4 CDN check this

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
    <link rel="icon" href="<%= BASE_URL %>favicon.ico" />

    <link
      rel="stylesheet"
      href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
    />
    <title><%= htmlWebpackPlugin.options.title %></title>

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
  </head>
  <body>
    <noscript>
      <strong
        >We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
        properly without JavaScript enabled. Please enable it to
        continue.</strong
      >
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

Add bootstrap 4 cdn inside index.html file

2. Install Vue Js Vuelidate plugin packages using NPM package manager inside terminal.
Open terminal -> Go to project root directory and write
npm install vuelidate

Go to project root directory and run npm run vuelidate command on terminal
Install Vuelidate plugin using npm manager

3. After installation of Vue Js Vuelidate plugin, Now let’s import the library and use as a Vue plugin to enable the functionality globally on all components containing validation configuration.
Go inside src folder -> Open main.js file

import Vue from "vue";
import App from "./App.vue";
import Vuelidate from "vuelidate";

Vue.use(Vuelidate);

Vue.config.productionTip = false;

new Vue({
  render: (h) => h(App),
}).$mount("#app");

Import Vuelidate on main.js file

4. Now, Go to component file where you want to use model based Vue Js form Validation using Vuelidate plugin and initialize data models.

Go to component file and initialize the data models
Go to component file and initialize the data models

5. Import Vuelidate into the component file so we can use it and then add validations options to create a key for each inputs.

import {
  required,
} from "vuelidate/lib/validators";

export default {
  name: "HelloWorld",
  data() {
    return {
      name: "",
    };
  },
  validations: {
    name: {
      required,
      isUnique(value) {
        if (value === "") return true;
        var re = /^[a-zA-Z]+(?:\s[a-zA-Z]+)+$/;
        return new Promise((resolve) => {
          resolve(re.test(value));
        });
      },
    },
  },
};

Import vuelidate in component file and add validations keys
Import Vuelidate in component file and add validations keys for each input

6. Now, remove all the code inside template tag and add Vuelidate syntax to validate input.

<div class="row mb-4">
    <div class="col-4">
      <label class="color_text required">Full name</label>
    </div>
    <div class="col-8">
      <input
        class="form-control"
        type="text"
        v-model.trim="$v.name.$model"
        :class="{
          'is-invalid': $v.name.$error,
          'is-valid': !$v.name.$invalid,
        }"
      />
    
      <div class="valid-feedback"></div>
      <div class="invalid-feedback">
        <span v-if="!$v.name.required">Full Name is required</span>
        <span v-if="!$v.name.isUnique"
          >Invalid Full name e-g John Smith</span
        >
      </div>
    </div>
</div>

Inside template add Vuelidate syntax to validate input
Inside template tag add Vuelidate syntax to validate input

Awesome 🙂 you have successfully added Vuelidate.

Note:
Vue comes with a modifier for v-model: v-model.lazy. This will only evaluate the binding once the user has completed the task with the input.

7. Import and add validators and Vuelidate syntax input for all other models we are using.

To check the list of all provided validators by Vuelidate you can visit here

Finally lets see the custom validation by using Vue Js Vuelidate library.

Simply add the function inside your script and then assign it inside the validators of model where you want to use it.

At last lets implement form submit.

Add button of type submit at the end of form then binds the submit event to submitForm method like: @submit.prevent="submitForm"
Note: .prevent use to prevents the default browser form submit behavior.

And then add method inside script. Check the whole code for component file.

<template>
  <div class="hello container p-5">
    <div class="form-card">
      <h2 class="fs-title mt-2 mb-2">
        Vue Js Form validation using Vuellidate
      </h2>

      <form @submit.prevent="submitForm">
        <div>
          <div class="row mb-4">
            <div class="col-4">
              <label class="color_text required">Full name</label>
            </div>
            <div class="col-8">
              <input
                class="form-control"
                type="text"
                v-model.trim="$v.name.$model"
                :class="{
                  'is-invalid': $v.name.$error,
                  'is-valid': !$v.name.$invalid,
                }"
              />

              <div class="valid-feedback"></div>
              <div class="invalid-feedback">
                <span v-if="!$v.name.required">Full Name is required</span>
                <span v-if="!$v.name.isUnique"
                  >Invalid Full name e-g John Smith</span
                >
              </div>
            </div>
          </div>
          <div class="row mb-4">
            <div class="col-4">
              <label class="color_text required">Username</label>
            </div>
            <div class="col-8">
              <input
                class="form-control"
                type="text"
                v-model.trim="$v.username.$model"
                :class="{
                  'is-invalid': $v.username.$error,
                  'is-valid': !$v.username.$invalid,
                }"
              />

              <div class="valid-feedback"></div>
              <div class="invalid-feedback">
                <span v-if="!$v.username.required">Username is required</span>
                <span v-if="!$v.username.alpha"
                  >Should only contains alphabets</span
                >
              </div>
            </div>
          </div>
          <div class="row mb-4">
            <div class="col-4">
              <label class="color_text required"> Date</label>
            </div>
            <div class="col-8">
              <div class="input-group">
                <input
                  class="form-control"
                  id="birthdate"
                  v-model.trim="$v.birthdate.$model"
                  :class="{
                    'is-invalid': $v.birthdate.$error,
                    'is-valid': !$v.birthdate.$invalid,
                  }"
                  type="date"
                  placeholder=""
                />

                <div class="valid-feedback"></div>
                <div class="invalid-feedback">
                  <span v-if="!$v.birthdate.required"
                    >Birthdate is required</span
                  >
                </div>
              </div>
            </div>
          </div>
          <div class="row mb-4">
            <div class="col-4">
              <label class="color_text required">Email</label>
            </div>
            <div class="col-8">
              <input
                class="form-control"
                v-model.trim="$v.email.$model"
                :class="{
                  'is-invalid': $v.email.$error,
                  'is-valid': !$v.email.$invalid,
                }"
              />

              <div class="valid-feedback"></div>
              <div class="invalid-feedback">
                <span v-if="!$v.email.required">Email is required</span>
                <span v-if="!$v.email.email"
                  >Invalid Email e-g example@company.com</span
                >
              </div>
            </div>
          </div>
          <div class="row mb-4">
            <div class="col-4">
              <label class="color_text required">Url</label>
            </div>
            <div class="col-8">
              <input
                class="form-control"
                v-model.trim="$v.url.$model"
                :class="{
                  'is-invalid': $v.url.$error,
                  'is-valid': !$v.url.$invalid,
                }"
              />

              <div class="valid-feedback"></div>
              <div class="invalid-feedback">
                <span v-if="!$v.url.required">Url is required</span>
                <span v-if="!$v.url.url">Invalid Url format</span>
              </div>
            </div>
          </div>
          <div class="row mb-4">
            <div class="col-4">
              <label class="color_text required">Age</label>
            </div>
            <div class="col-8">
              <input
                class="form-control"
                v-model.trim="$v.age.$model"
                :class="{
                  'is-invalid': $v.age.$error,
                  'is-valid': !$v.age.$invalid,
                }"
              />

              <div class="valid-feedback">Your age is valid</div>
              <div class="invalid-feedback">
                <span v-if="!$v.age.required">Age is required</span>
                <span v-if="!$v.age.between">Age should be between 15-55</span>
              </div>
            </div>
          </div>
          <div class="row mb-4">
            <div class="col-4">
              <label class="color_text required">Phone Number</label>
            </div>
            <div class="col-8">
              <div class="input-group mb-3">
                <input
                  class="form-control"
                  type="text"
                  v-model.trim="$v.mobile.$model"
                  :class="{
                    'is-invalid': $v.mobile.$error,
                    'is-valid': !$v.mobile.$invalid,
                  }"
                />

                <div class="valid-feedback"></div>
                <div class="invalid-feedback">
                  <span v-if="!$v.mobile.required"
                    >Mobile number is required</span
                  >
                  <span v-if="!$v.mobile.numeric"
                    >Mobile number should only be numeric</span
                  >
                  <span v-if="!$v.mobile.minLength"
                    >Mobile number min length should be 3</span
                  >
                  <span v-if="!$v.mobile.maxLength"
                    >Mobile number max length should be 10</span
                  >
                </div>
              </div>
            </div>
          </div>
          <div class="row mb-4">
            <div class="col-4">
              <label class="color_text required">Relationship to student</label>
            </div>
            <div class="col-8">
              <div class="form-group">
                <select
                  v-model.trim="$v.relation_to_student.$model"
                  :class="{
                    'is-invalid': $v.relation_to_student.$error,
                    'is-valid': !$v.relation_to_student.$invalid,
                  }"
                  class="form-control select_center_text"
                  id="relationSelect"
                >
                  <option value="Father">Father</option>
                  <option value="Mother">Mother</option>
                  <option value="Brother">Brother</option>
                  <option value="Brother">Cousin</option>
                  <option value="Sister">Sister</option>
                  <option value="Uncle">Uncle</option>
                  <option value="Aunt">Aunt</option>
                </select>

                <div class="valid-feedback"></div>
                <div class="invalid-feedback">
                  <span v-if="!$v.relation_to_student.required"
                    >Relationship to student is required</span
                  >
                </div>
              </div>
            </div>
          </div>
          <div class="row mb-4">
            <div class="col-4">
              <label class="color_text required">Password</label>
            </div>
            <div class="col-8">
              <input
                class="form-control"
                type="password"
                v-model.trim="$v.password.$model"
                :class="{
                  'is-invalid': $v.password.$error,
                  'is-valid': !$v.password.$invalid,
                }"
              />

              <div class="valid-feedback"></div>
              <div class="invalid-feedback">
                <span v-if="!$v.password.required">Password is required</span>
                <span v-if="!$v.password.withUnderScore"
                  >Should contains underscore e-g '_'</span
                >
              </div>
            </div>
          </div>
          <div class="row mb-4">
            <div class="col-4">
              <label class="color_text required">Confirm password</label>
            </div>
            <div class="col-8">
              <input
                class="form-control"
                v-model.trim="$v.confirm_password.$model"
                :class="{
                  'is-invalid': $v.confirm_password.$error,
                  'is-valid':
                    password != '' ? !$v.confirm_password.$invalid : '',
                }"
              />

              <div class="valid-feedback"></div>
              <div class="invalid-feedback">
                <span v-if="!$v.confirm_password.sameAsPassword"
                  >Confirm password does not match</span
                >
                <span v-if="!$v.confirm_password.required"
                  >Confirm Password is required</span
                >
              </div>
            </div>
          </div>
        </div>
        <button type="submit" class="btn btn-success">
          Submit
        </button>
      </form>
    </div>
  </div>
</template>

<script>
//const withSpace = (value) => value.indexOf(" ") !== -1;
const withUnderScore = (value) => value.indexOf("_") >= 0;

import {
  required,
  email,
  minLength,
  maxLength,
  between,
  alpha,
  numeric,
  url,
  sameAs,
} from "vuelidate/lib/validators";

export default {
  name: "HelloWorld",
  props: {
    msg: String,
  },
  data() {
    return {
      name: "",
      email: "",
      birthdate: "",
      age: "",
      mobile: "",
      username: "",
      url: "",
      relation_to_student: "",
      password: "",
      confirm_password: "",
    };
  },
  methods: {
    submitForm() {
      this.$v.$touch();

      if (!this.$v.$invalid) {
        // SUCCESS
        alert("Form is valid");
      }
    },
  },
  validations: {
    name: {
      required,
      isUnique(value) {
        if (value === "") return true;
        var re = /^[a-zA-Z]+(?:\s[a-zA-Z]+)+$/;
        return new Promise((resolve) => {
          resolve(re.test(value));
        });
      },
    },
    username: {
      required,
      alpha,
    },
    birthdate: {
      required,
    },
    email: {
      required,
      email,
    },
    age: {
      required,
      between: between(15, 55),
    },
    url: {
      required,
      url,
    },
    relation_to_student: {
      required,
    },
    mobile: {
      required,
      numeric,
      minLength: minLength(3),
      maxLength: maxLength(10),
    },
    password: {
      required,
      withUnderScore,
    },
    confirm_password: {
      required,
      sameAsPassword: sameAs("password"),
    },
  },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.is-invalid ~ .invalid-feedback,
.is-invalid ~ .invalid-tooltip,
.was-validated :invalid ~ .invalid-feedback,
.was-validated :invalid ~ .invalid-tooltip {
  display: grid !important;
}
</style>

Share your love with us 😉

Click on a star to rate it!

Average rating 5 / 5. Vote count: 1

No votes so far! Be the first to rate this post.

Leave a Reply