// Tutorial //

How To Use Form Validation in Vue

Published on December 12, 2019
Default avatar
By Victor Jioke
Developer and author at DigitalOcean.
How To Use Form Validation in Vue

While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.

Introduction

Almost every web application makes use of forms in some way, as such developers always have to tackle form validations. If you are a new developer, it can be hard deciding how best to approach this. Depending on the stack you are using, there are many of options to choose from.

In this tutorial we will learn how to you can implement form validation in your form with Vue.

We want to show the error message as soon as the user hits the submit button—no need to send a response to the server—as the validation is on the client-side. Having a client-side validation does not remove the need of validating requests on your server—that is very important too.

Building the First Example

With that established, let’s build our form. First, our App component will only render the Register component:

<div id="app">
  <div>
    <Register />
  </div>
</div>

The script section will look like this:

new Vue({
  el: "#app"
})

For the Register component we want to show a form with inputs fields:

  <div>
    <h2>Register</h2>

    <form @submit.prevent="register" method="post">
      <div>
        <label>Name:</label>
        <input type="text" v-model="user.name" />
        <div>{{ errors.name }}</div>
      </div>
      <div>
        <label>Phone:</label>
        <input type="text" v-model="user.phone" />
        <div>{{ errors.phone }}</div>
      </div>
      <div>
        <label>Email:</label>
        <input type="text" v-model="user.email" />
        <div>{{ errors.email }}</div>
      </div>
      <div>
        <label>Password:</label>
        <input type="password" v-model="user.password" />
        <div>{{ errors.password }}</div>
      </div>
      <div>
        <button type="submit">Submit</button>
      </div>
    </form>
  </div>

The .prevent method is used to stop the default behavior of the form when a submission is made. The form has four inputs field for name, email, phone, and password. All of these will need their own validations. If there is an error with any of the inputs, it will be displayed below the input field.

Since each field is unique, we need to ensure the validation is suited to match their uniqueness. A general one is that none of the fields should be empty. We can check for this using !field.length, where field will equate to any of the input fields we have. To keep our code clean, the validators will be defined outside the Vue instance. If you are building this in an app that is scaffolded using Vue CLI, it means you’ll have the validators in a separate file.

const validateName = name => {
  if (!name.length) {
    return { valid: false, error: "This field is required" };
  }
  return { valid: true, error: null };
};

const validatePhone = phone => {
  if (!phone.length) {
    return { valid: false, error: 'This field is required.' };
  }

  if (!phone.match(/^[+][(]?[0-9]{1,3}[)]?[-\s.]?[0-9]{3}[-\s.]?[0-9]{4,7}$/gm)) {
    return { valid: false, error: 'Please, enter a valid international phone number.' };
  }

  return { valid: true, error: null };
}

const validateEmail = email => {
  if (!email.length) {
    return { valid: false, error: "This field is required" };
  }
  if (!email.match(/^\w+([.-]?\w+)_@\w+(_[_.-]?\w+)_(.\w{2,3})+$/)) {
    return { valid: false, error: "Please, enter a valid email." };
  }
  return { valid: true, error: null };
};

const validatePassword = password => {
  if (!password.length) {
    return { valid: false, error: "This field is required" };
  }
  if (password.length < 7) {
    return { valid: false, error: "Password is too short" };
  }
  return { valid: true, error: null };
};

For unique fields like email and phone number, we make use of regex to make sure it matches a specific pattern. Each validator is a function that will receive the input field as a parameter. As you can see from above, each function returns valid and error. This is what we will use to determine if a form should be submitted. To see that in action, here is how the Register component will look:

Vue.component('register', {
  template: '#register',
  data() {
    return {
      user: {
        email: '',
        password: '',
        name: '',
        phone: ''
      },
      valid: true,
      success: false,
      errors: {},
      message: null
    }
  },
  methods: {

    register() {
      this.errors = {}

      const validName = validateName(this.user.name);
      this.errors.name = validName.error;
      if (this.valid) {
        this.valid = validName.valid
      }

      const validPhone = validatePhone(this.user.phone);
      this.errors.phone = validPhone.error;
      if (this.valid) {
        this.valid = validPhone.valid
      }

      const validEmail = validateEmail(this.user.email);
      this.errors.email = validEmail.error;
      if (this.valid) {
        this.valid = validEmail.valid
      }

      const validPassword = validatePassword(this.user.password)
      this.errors.password = validPassword.error
      if (this.valid) {
        this.valid = validPassword.valid
      }

      if (this.valid) {
        alert('HURRAAYYY!! :-)\n\n' + JSON.stringify(this.user))
      }
    }
  }
})

If a validator returns an error for any of the fields, the error returned is saved in the errors.fieldName—where fieldName is the name of the input field, and then displayed for the user to see what went wrong.

When all fields return valid as true, we can then go ahead to submit the form. For this tutorial, we are displaying an alert box.

Using Joi

Joi allows you to build schemas for JavaScript objects, which can be used to validate inputs. It is often used when working with Express and Restify. We can use it in this tutorial by defining a schema for our Register component.

Here is the schema:

import Joi from "joi";

const phoneRegex = /^[+]?[(]?[0-9]{3}[)]?[-\s.]?[0-9]{3}[-\s.]?[0-9]{4,7}$/gm;
const phone = Joi.string().regex(phoneRegex)
  .options({
    language: {
      string: {
        regex: {
          base: 'must be a valid phone number'
        }
      }
    }
  });

const userValidation = {};
userValidation.create = {
  first_name: Joi.string().min(2).max(24).required(),
  email: Joi.string().email().required(),
  password: Joi.string().min(7).required(),
  phone: phone.required()
};

We can then use the schema in our Register component to validate the inputs of the user:

Vue.component('register', {
  template: '#register',
  data() {
    return {
      user: {
        name: '',
        email: '',
        password: '',
        phone: ''
      },
      valid: false,
      success: false,
      errors: {},
      issues: {}
    }
  },
  methods: {
    // method that validates the user input using the schema
    validate(value, schema) {
      const result = Joi.validate(value, schema, { abortEarly: false });
      if (result.error) {
        result.error.details.forEach((error) => {
          this.issues[error.path[0]] = error.message;
        });
        return false;
      }
      return true;
    },

    register() {
      // validation method is called and passed the inputs and schema
      this.validate(this.user, userValidation.create);
        if (Object.keys(this.issues).length > 0) {
          this.errors = this.issues;
          return false;
        }
        alert('HURRAAYYY!! :-)\n\n' + JSON.stringify(this.user))
    }
  }
})

We declare a validate method that will accept the user inputs and the schema we have defined. If errors are found after the validation we will return a false and the errors encountered. If there are no errors, we display the alert box as we did before.

Conclusion

VeeValidate and Vuelidate are alternatives you can also make use of when handling form validation in your Vue application.

If you’ve enjoyed this tutorial and our broader community, consider checking out our DigitalOcean products which can also help you achieve your development goals.

Learn more here


About the authors
Default avatar
Developer and author at DigitalOcean.

Still looking for an answer?

Was this helpful?
Leave a comment

This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!