fbpx

Getting Smart with Vue Form Validation – Vuelidate Tutorial

Vue Form Validation is an essential part of any form system. You need to be sure that people are submitting data that your app can work with! 

Whether it’s ensuring users have a strong password or  checking that a message isn’t too long, input validation is everywhere in web apps. 

Luckily for Vue developers, the Vuelidate library does most of the hard work for us. We don’t have to write hundreds of conditionals to verify our inputs. We get to work smarter instead of working harder. 

By the end of this tutorial, you should…

  • Know what Vuelidate is 
  • Get it installed 
  • Build a basic form validation
  • Know some advanced Vuelidate topics

Vuelidate is a great library for all Vue developers, so let’s dive right in and get coding. 

What is Vuelidate?

Vuelidate provides model-based validation for Vue projects. 

It’s a simple, yet powerful way to add form validation into your projects because it has dozens of built in validators. Some of the basics include…

  • required – value cannot empty 
  • minLength/maxLength – provides a min or max length for a value
  • email – value must be a valid email address format
  • alpha – value only accepts the alphabet
  • numeric – value only accepts numbers

This is only a short list of the built in validators that Vuelidate offers. For a complete list, be sure to go to the Vuelidate docs

A basic Vue form validation example

Now that we know what Vuelidate is and have some sort of idea what it can do, let’s implement it.

First, we have to install Vuelidate and include it in our src/main.js file. Run npm install vuelidate --save to add it to your Vue project. Then, in our main.js file, we need these two lines to get Vuelidate into our app. 

import Vuelidate from 'vuelidate'

Vue.use(Vuelidate)

Okay. Vuelidate is now in our app, but how do we use it? 

For this tutorial, I used built a user signup component that has 4 fields.

  1. Username
  2. Email Address
  3. Password
  4. Confirm Password

Here is the code for our component. I’ve added some minimal styles to make our app feel less bland, but you could always use one of many Vue input libraries.

<template>
  <div>
    <h2>Create Your Account</h2>
    <input class="form__input" type='text' placeholder='Email'/>
    <input class="form__input" type='text' placeholder='Username'/>
    <input class="form__input" type='password' placeholder='Password'/>
    <input class="form__input" type='password' placeholder='Confirm Password'/>
    <div class='form__submit' @click='submitForm'>
      Submit
    </div>
  </div>
</template>

<script>
export default {
  data () {
  },
  validations: {
  },
  methods: {
    submitForm () {
      alert('Form has been submitted')
    }
  }
}
</script>

<style scoped>
.form__input {
  border: none;
  outline: none;
  border-bottom: 1px solid #eee;
  font-size: 1em;
  padding: 5px;
  display: block;
  margin: 10px 0 5px 0;
}

.form__error {
  color: red;
  font-size: 0.75em;
  padding-left: 10px;
}

.form__submit {
  background-color: #0033cc;
  display: inline-block;
  color: white;
  padding: 10px 20px;
  text-align: center;
  cursor: pointer;
  margin-top: 30px;
}
</style>

If we check out our component, it should look like this. 

Our form!

Adding Vuelidate to our form

Vuelidate adds new validations property to our options object. This validations object will have the same properties as the data that we are gathering. It’s used in combination with the data property. 

If we want to make all of our fields required, first we have to import the required validator from Vuelidate.  Then, we fill our data and validations objects.

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

export default {
  data() {
    return {
      email: '',
      username: '',
      password: '',
      confirmPassword: ''
    }
  },
  validations: {
    email: {
      required
    },
    username: {
      required
    },
    password: {
      required
    },
    confirmPassword: {
      required
    }
  }
}

Now that we have a basic validator, let’s make our form use it. First, we have to know that Vuelidate exposes a $v object on Vue instance containing all of our validations – which can be accessed like this.$v

If we print out the $v object, we see that it looks like this

Our $v object from Vuelidator

Then, if we look inside one of these validation objects – email, for example. We’ll see a $model property. 

Inside one of our objects

$model binds our validation to our data. This is how we can use v-model in our form – by binding our input to the $v.propertyName.$model property. 

Here’s how you would do add v-model to the email property. All the other inputs follow the same format.

<input class="form__input" type='text' placeholder='Email' v-model.trim='$v.email.$model'/>

Note: we can use the .trim modifier for our v-model to eliminate trailing/leading whitespaces in our validations. Just think about whether or not spaces are important to preserve. I only added this onto the email input.

Showing errors to our user

If we take another look at our $v.email object, we’ll see that each of the validators we include creates a new boolean. For example, our required property looks like this inside the object. 

The required validator creates a boolean

The value represents whether or not the validation has been met. For required, it will be false if the trimmed value is empty and true otherwise.

We can use this boolean and a v-if to show input errors to our user.

For example, if we wanted to display “This field is required” whenever the email input is empty, it could be done like this.

<div class="form__error" v-if='!$v.email.required'>
      *This field is required.
</div>

Now, whenever our field is empty (and does not meet Vuelidate’s required validation), this div will be visible. 

Our first validator is all set up!

Using more advanced form validations

Vuelidate offers many additional built-in validators and we’ll need some of them for this example. Let’s go through each input and add the validators we need. 

Username

For our implementation of a sign up component, we want to impose the following rules on our username input.

  • minLength – sets a minimum length for our usernames
  • maxLength – sets a maximum length for our usernames
  • alphaNum – limits our usernames to alpha-numeric values 

Adding these three built-in validators, our new email validation object will look like this. Keep in mind that we have to import all our these validators like we did with required. 

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

// ...

validations: {
   username: {
      required,
      minLength: minLength(4),
      maxLength: maxLength(12),
      alphaNum
   }
}

Then, just like we did earlier, we can provide some feedback to the user by adding more div statements. 

<div class="form__error" v-if='!$v.username.required'>
      *This field is required.
</div>
<div class="form__error" v-if='!$v.username.minLength'>
      *Username must be at least 4 characters
</div>
<div class="form__error" v-if='!$v.username.maxLength'>
      *Username must be 12 characters or less
</div>
<div class="form__error" v-if='!$v.username.alphaNum'>
      *Username must be alphanumberic with no whitespace
</div>

One thing that we cannot do with built in validators is ensure that there is no whitespace inside our username. Later, we’ll add a custom validation to check this.

Email

Besides required, the other main validator this input needs is email – which determines whether or not the value is a valid email address.

email: {
      required,
      email
}

Password

For this example, we’ll keep it basic and say that the only password requirement is that it’s at least 6 characters long so once again we’ll use the minLength.

password: {
      required,
      minLength: minLength(6)
}

In production, we can create a custom validation that checks the strength of the password to determine if it’s strong enough. 

Confirm Password

The only real requirement for the confirmPassword field is that it matches the first password. All of the other requirements (length, in our case) should be taken care of by the first password input. 

We can check equality between two inputs using the sameAs validator with the name of the password validation object (password) as its argument.

confirmPassword: {
      required,
      sameAs: sameAs('password')
}

Submitting our Vue Form

Even though we have all of these validators set up, there’s nothing stopping someone from just submitting the form anyways. 

We have to actually check if there are any errors on the form when we try to submit.

Thankfully, this is extremely simple using the $v.$anyError property. This boolean value is true if any of the elements in Vuelidator are invalid and false is everything is good to go.

Inside our submitForm method, we just have to check if there is any error on our form. If this.$v.$anyError is false, we’re all good to submit!

submitForm () {
      if (!this.$v.$anyError) {
        // actually submit form ...
        alert('Form submitted')
      } else {
        alert('Please fix errors and try again.')
      }
}

Advanced Vuelidate Techniques

With a basic Vuelidate example done, let’s go over a few more complicated techniques that we can use to work with more complex forms. 

In this tutorial, we’ll be covering two advanced features of Vuelidate.

  • Making custom validators 
  • Performing asynchronous validation

It’s also worth it to note that your form validation can be extracted into mixins and reused across components. 

Making custom validators

Despite coming with dozens of built-in validators, there will be times when those don’t cover your project’s needs. 

Even in our simple example form, I think it would be very useful to have a validator that makes sure there is no whitespace in the username input. 

To make a custom validator, all we have to do is write an ordinary Javascript method that returns a boolean. 

For example, for our no whitespace validator, the method would look like this. 

const noWhitespace = (value) => value.indexOf(' ') === -1

Then, we can include this function inside our username validator just like the built-in ones. 

username: {
      required,
      minLength: minLength(4),
      maxLength: maxLength(12),
      alphaNum,
      noWhitespace
}

After that, it works just like all of the other validators we’ve seen.  

Performing asynchronous validation

For most advanced apps, you’ll eventually need asynchronous validation – primarily when we are working with databases. Here are two examples of times async is necessary. 

  • Checking if a username is available
  • Checking if an email is already linked to an account

Like working with custom validators, we’ll have to write a Javascript method. This time, however, we want to return a promise. 

For this example, we’ll be checking to see if the username is available. Because we don’t have a database, let’s just use setTimeout and some hardcoded values.

const isUsernameAvailable = (value) => {
  
  if (value === '') {
    return true
  }
  const taken = ['username', 'matt', 'matthew']
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(!taken.includes(value))
    }, 500)
  })
}

Now, if we check our database, we’ll see that our hardcoded set of “used” usernames will result in an error. 

Bonus Tips – style your forms

One of the best ways to make your form more user-friendly is to provide slick visual feedback. There are several intuitive ways to do this. We’ll just go over a couple here. 

1. Add an error class to your inputs

Like we’ve seen before, each validated property in Vuelidate has an $anyError property. 

If we use Vue’s class binding syntax, we can easily add/remove an error class depending on the value of the $anyError property. 

For example, if we wanted to add an form__input-error class if a value was invalid, we could do so like this. 

<input 
      class="form__input" 
      :class='{"form__input-error": $v.email.$error}'
      type='text' 
      placeholder='Email' 
      v-model.trim='$v.email.$model' 
/>

Then, we can style the class form__input-error however we want.

2. Add a little shake when a value is invalid

This one is a little bit more complicated than simply adding a class. For starters, this is what we’ll be making

It’s best to perform this visual cue on the blur event – meaning that an input has lost focus. This way, it’s super clear that the user’s last action (editing the input) was the one that caused the error. 

To accomplish this, we’ll need two things:

  1. A CSS animation
  2. A CSS class that runs this animation

Let’s add the following to our CSS code. 

.form__input-shake {
  animation: shake 0.2s;
  animation-iteration-count: 3;
}

@keyframes shake {
  0% { transform: translateX(0px)  }
  25% { transform: translateX(2px) }
  50% { transform: translateX(0px)  }
  75% { transform: translateX(-2px) }
  100% { transform: translateX(0px)  }
}

Next, let’s create a checkIsValid function that checks if a validator has any error, if it does – it will add the animation class to our element, wait until the animation is done (0.2s * 3 = 600ms), and then remove the class so it can be added again later. 

checkIsValid (val, event) {
      if (val.$anyError) {
        event.target.classList.add('form__input-shake')
        setTimeout(() => {
          event.target.classList.remove('form__input-shake')
        }, 600)
      }
}

Finally, we have to actually call this function whenever our input is blurred. Using Vue’s event handlers, it’s a little bit like this. 

<input 
      class="form__input" 
      :class='{"form__input-error": $v.email.$error}'
      type='text' 
      placeholder='Email' 
      v-model.trim='$v.email.$model' 
      @blur='checkIsValid($v.email, $event)'
/>

Conclusion to Vue Form Validation

There you go! 

Vuelidate is an extremely powerful tool and can save you from having to write a complicated reactive system all on your own. I’ve personally used it in countless personal projects. 

Now, you should now have an idea of how to get started in Vuelidate. We’ve covered:

  • Installing Vuelidate
  • Building an example form 
  • Adding some of Vuelidate’s advanced features
  • Styling a form to create a better user experience

Let me know how you’re using Vuelidate in your projects. I’d love to hear your creative solutions 🙂