Vue JS Form Validation Using Options API

Javascript Vue.js

In this article, I’m going to cover how to validate a simple sign-up form on the client-side in vue.js using Options API.

I also have an article on how to do Form Validation using Vue JS 3 Composition API for Sign-up and login pages, in case if you’re interested in that as well.

As you can see from the final output below, when I press the sign-up button with all the input fields empty, error messages appear above the sign-up form.

When I enter the wrong email address, I get a different validation error, this time saying invalid email.

Finally, I am going to restrict the length of the password to be at least 8 characters.

In this process, I’ll be showing you how to:

  • Create and bind user object properties to the input fields
  • Attach an event to the form or to a button inside the form
  • Show you different ways of preventing the default form submission behavior
  • Validate all the input fields
  • Show how to accumulate all the errors in the errors array and show them on the view

I’m very excited to show you them all! It is a lot of cover so let’s get started.

This is a part of a series and I’ve already covered how to design a sign-up form in one of my other articles.

Create User Object 

In the SignUp.vue component, add script tags in between the template and style tags.

Then declare the export default object. This is where we’re going to add all of our JavaScript code for this component.

Next, define a data model, which is a function that returns an object.

<script>
  export default {
    data() {
      return {}
    },
</script>

Then, declare a property called user and the value of the user property is going to be a JavaScript object.

In there, I am going to create three properties:

  • Name
  • Email
  • Password
<script>
  export default {
    data() {
      return {
        user: {
          name: "",
          email: "",
          password: "",
        },
      };
    },
</script>

Now that we have the properties we need, let’s go ahead and bind them to the input fields.

Bind User Object To The Input Fields 

Go to the name input field at the top and before the self-closing angle bracket, bind the name property from the user object to this name input field using v-model directive.

<!-- FULL NAME -->
<div class="field">
  <div class="ui left icon input big">
    <i class="user icon"></i>
    <input type="text" placeholder="Full Name" v-model="user.name" />
  </div>
</div>

Note: The .name here must be matched with the name property inside the user object.

Let’s bind the other two in the same way.

<!-- EMAIL -->
<div class="field">
  <div class="ui left icon input big">
    <i class="mail icon"></i>
    <input type="email" placeholder="Email" v-model="user.email" />
  </div>
</div>
<!-- PASSWORD -->
<div class="field">
  <div class="ui left icon input big">
    <i class="lock icon"></i>
    <input type="password" placeholder="Password" v-model="user.password" />
  </div>
</div>

It’s important to create a property first inside the data() model before binding it to the view, otherwise, Vue will throw the undefined property error which looks something like this.

Attach A Click Event To The Sign-Up Button

Next, attach a click event to the sign-up button.

Go to the sign-up button element and before the closing angle bracket in the starting button tag, bind a click event to the button using v-bind:click=””.

Then add the callback function called signUpButtonPressed.

<button class="ui button big red fluid" v-bind:click="signUpButtonPressed">SIGN UP</button>

Actually there is a shortcut for binding a click event to a button. Instead of using v-bind:click, I can simply use @click which will do the exact same thing but looks much cleaner.

<button class="ui button big red fluid" @click="signUpButtonPressed">SIGN UP</button>

Declare The Sign-Up Button Callback Function

In Vue 2 Options API, all of the functions must be declared inside the methods object.

So inside the methods object declare a signUpButtonPressed callback function.

export default {
  data() {
    ...
  },
  methods: {
    signUpButtonPressed() {
      console.log("Sign up button pressed!")
    }
  }
}

Let’s run the app to check if the sign-up button works.

Open up the Terminal, go to the project directory and run npm run dev. Then go to the URL on the browser and make sure to go to the /signup route.

Let’s open up the browser console to see if the button press is working.

Oh wait! Something has happened?

Two things have happened. There is a ? mark added to the URL as well as we cannot see the console log message.

However, when I click the sign-up button a second time, I can see the message in the console as expected.

Form Submit Default Behaviour

But the question is why is it not working the first time?

When I click the sign-up button for the first time, it basically reloads the page and tries to submit the form data via GET request – that’s why we see the question mark (?) in the URL.

This is the default behavior of a <form> element when a button inside is pressed.

As you can see, even though I did not add the type=”submit” attribute to the button element, the form can still be submitted.

Also, you can see the console message is actually visible on the browser console for a second and then it quickly disappears as the page gets reloaded.

There are THREE ways to prevent the default form submission behaviour:

  • Using preventDefault() method on the click event object
  • Adding prevent modifier when attaching a click event to the sign-up button
  • Adding prevent modifier when attaching submit event to the form element. Alternatively, I can use preventDefault() method as well, similar to the click event.

PreventDefault Method On The Event Object

The first option is to call the preventDefault() method on the click event object that is passed into the signUpButtonPressed callback function.

To access the event object inside the signUpbuttonPressed function, pass the parameter called e in-between the parentheses.

export default {
  data() {
    ...
  },
  methods: {
    signUpButtonPressed(e) {
      e.preventDefault();
    }
  }
}

Now, call the preventDefault() method on the event object by typing event.preventDefault();

This works great!

And I can see the message on the console for the first time onwards when I click the sign-up button.

Also, there is no question mark in the URL as the page does not get reloaded.

Nice.

Add Prevent Modifier To The Sign-up Button

Instead of using the preventDefault() function on the event object, I can simply add the prevent modifier to the click event of the sign-up button in the template using the .notation.

<button class="ui button big red fluid" @click.prevent="signUpButtonPressed">SIGN UP</button>

That’s it.

Now, I do not have to use the e.preventDefault() function anymore so I can get rid of it as well as the event parameter that was passed into the signUpButtonPressed function.

Nice.

Add Prevent Modifier To The Form Element

The third option is to attach the submit event to the form element rather than attaching a click event to the button inside.

So, get rid of the click event from the button.

<button class="ui button big red fluid">SIGN UP</button>

Then, go to the starting form tag and attach the submit event with the same callback function that I’ve used in the button before.

<form class="ui form" @submit="signUpButtonPressed">
...
</form>

To stop the default submission, we can use either preventDefault() method on the event object or just use the prevent modifier to the submit event similar to before.

So add .prevent right after submit.

<form class="ui form" @submit.prevent="signUpButtonPressed">
...
</form>

Let’s try it on the browser now.

You can see it brings the exact same results like before.

Check If The Input Fields Are Empty 

First I am going to check if the name input field is empty inside the signUpButtonPressed function.

export default {
  data() {
    ...
  },
  methods: {
    signUpButtonPressed(e) {
      if (this.user.name == "") {
         console.log("User name is empty");
      }
    }
  }
}

This works but there is a better way.

Instead of using an empty string, I can simply do the same check by just adding an exclamation mark in front of this keyword to turn this into a boolean value and get rid of == “”

This condition will be true when the value of the name property is empty.

if (!this.user.name) {
      console.log("User name is empty");
}

Let’s do the same to the email and password fields.

export default {
  data() {
    ...
  },
  methods: {
    signUpButtonPressed(e) {
      if (this.user.name == "") {
         console.log("User name is empty");
      }
      if (this.user.email == "") {
         console.log("Email is empty");
      }
      if (this.user.password == "") {
         console.log("Password is empty");
      }
    }
  }
}

Let’s test it out in the browser.

When I click the sign-up button without typing anything, I get all three error messages on the browser console.

Nice.

We’ve successfully validated all of our input fields whether they are empty or not.

Next, let’s accumulate the error messages into an array so that we can show them later above the form.

Accumulate Error Messages

To do that, declare a property called errors with a value of empty array inside the data() model.

export default {
  data() {
    ...
    errors:[]
  },
  methods: {
   ...
  }
}

This is where I am going to accumulate all validation error messages when the sign-up button is pressed.

In the name if block, get rid of the console log message and push the error message to the error array instead.

Do the same to the other two.

export default {
  data() {
    ...
    errors:[]
  },
  methods: {
    signUpButtonPressed(e) {
      if (this.user.name == "") {
         this.errors.push("User name is empty");
      }
      if (this.user.email == "") {
         this.errors.push("Email is empty");
      }
      if (this.user.password == "") {
         this.errors.push("Password is empty");
      }
    }
  }
}

This time I am going to leave all of the input fields empty and hit the sign-up button and you can see all three error messages appear on the browser console.

Let’s see what happens when I click the sign-up button again.

Now I get 6 messages.

You may wonder why?

This is because every time I click the sign-up button, the error messages have been appended to the errors array.

To avoid this behaviour, we need to clear the items in the errors array every time the sign-up button is pressed.

So, inside the signUpButtonPressed, at the top before pushing any item to the errors array, clear the existing items by assigning an empty array to it.

export default {
  data() {
    ...
    errors:[]
  },
  methods: {
    signUpButtonPressed(e) {
      this.errors = [];
      if (this.user.name == "") {
         this.errors.push("User name is empty");
      }
      if (this.user.email == "") {
         this.errors.push("Email is empty");
      }
      if (this.user.password == "") {
         this.errors.push("Password is empty");
      }
    }
  }
}

Now I can only see three messages every time I click the sign-up button when all the inputs are empty.

When I type some text in the name field and hit the sign up button, I can only see two error messages representing the email and password fields in the errors array, none for the name field.

That’s exactly what we want.

Show The Errors On The View

Now that we have error messages in the errors array, let’s show them on the screen.

To do that, I am going to create a div above the starting form tag with three semantic ui css classes which are ui message red which will create an empty rounded red bordered box.

<div class="ui message red big" >
        <li v-for="(error, index) in errors" :key="index">{{ error }}</li>
</div>

      <form class="ui form" @submit.prevent="signUpButtonPressed">
...

Then, loop through the errors array here using the v-for directive.

Then bind the key attribute to the li element. The value of this will be unique.

So, get the index value of the array on each iteration and add it as a value of the key attribute.

After the error variable on the left, add a comma and type index which will have an index value on each iteration.

Whenever you have more than one variable on the left side of the loop signature, we need to wrap them in parentheses.

Then render the error messages in between the li tags using double curly braces.

When I click the sign-up button, we can see all the error messages on the view.

Nice.

But we do not want the error message box to be visible by default or whenever there is no error message.

Show/Hide Error Message Box

To show and hide the error message box,  I’m going to add a condition to the div that holds the error messages by checking to see if the length of the errors array is greater than zero or not.

<div class="ui message red big" v-show="errors.length > 0">
        <li v-for="(error, index) in errors" :key="index">{{ error }}</li>
</div>

      <form class="ui form" @submit.prevent="signUpButtonPressed">
...

This way the error message box is only visible when there is an actual error.

Let’s add data to all the input fields then click the sign-up button and I can see the validation error message below the email input field.

This is the browser default email validation that we do not want as we’re going to do our own email validation in just a moment.

To get rid of this, all we have to do is to add novalidate attribute to the starting form tag.

<form class="ui form" @submit.prevent="signUpButtonPressed" novalidate>
...
</form>

And I can see the error message box is hidden when all the input fields are not empty.

Great!

Email Validation 

For that, I am going to use a Regular expression function called validEmail() inside the methods object.

This function takes email as an argument and returns true if the email is valid. So pass the email parameter in between the parentheses.

Then, define a variable called re and assign the regular expression string to it.

validEmail(email) {
      var re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
      return re.test(email);
}

Then run the test() method on the re object passing the email as an argument and return it.

Now I can use this function to check the validity of the email.

In the if block where i check if the email field validity add else statement,

In there, create another if block.

 if (this.user.email == "") {
        this.errors.push("email is empty");
  } else {
      if (!this.validEmail(this.user.email)) {
          this.errors.push("Invalid Email");
      }
  }

This time I am going to check if the text entered in the email input field is a valid email or not.

Let’s call the validEmail function inside here. In vue 2 with Options API, we use this keyword when we call a function that is declared inside the methods object.

Then pass the user email which will be this.user.email

This function will return true if the email is valid but what we want is the opposite so add the exclamation mart in front of the statement.

That way we can push the invalid email error message to the errors array in there.

Password Validation

I am going to check if the password has at least 8 characters or not..

So similar to the email if block, add the else statement to the password if block.

Then, check if the password has at least 8 characters using the .lengh property on the password string.

 if (this.user.password == "") {
        this.errors.push("password is empty");
      } else {
        if (this.user.password.length < 8) {
          this.errors.push("password must be 8 characters long");
        }
}

If it’s true then add a new error message to the errors array inside this if block.

I can see the password validation length error when I’ve only three characters.

As soon as I type more than 8 characters and hit the sign-up button the error is gone.

Conclusion

Form validation on the client-side can be tricky in Vue JS with Options API but breaking them down into small pieces will make it more digestible and understandable which is the exact reason I wrote this artcile for.

I hope you have better understanding of how to

  • Create and bind user object properties to the input fields
  • Attach an event to the form or a button inside
  • Show you a few ways of preventing the default form submission behavior
  • Validate input fields if they are empty or not as well as email and password validation.
  • Showing how to accumulate all the errors in the errors array and show them on the view above the sign-up form.

Get Full Source Code Now!