Form validation, also known as form field validation, ensures that a user fills out all required fields in a web form. If a field has an invalid value, it will display an error message and prevent form submission until the values satisfy all the rules.
Template-driven validation is a type of form validation where validation rules are set directly in the form elements using directives.
To implement template-driven validations in Vue.js, we can use VeeValidate. VeeValidate is a plugin for Vue.js that allows you to validate input fields and display errors.
Here is an animated image of what you will build in this tutorial:
At the end of this tutorial, you will have a registration form that uses VeeValidate to validate the input fields.
This tutorial assumes knowledge of JavaScript strings and objects. Some familiarity with Vue will be beneficial but is not required. To learn more about Javascript, check out the How To Code in Javascript series.
We will focus on building a single local HTML file with references to various cloud-hosted libraries. It is possible to use @vue/cli
to create a Vue project and use a package manager to install vee-validate
; however, that approach is outside of this tutorial’s scope.
You will need the Vue.js framework and the VeeValidate library.
First, use your terminal to create a new file called register.html
:
- nano register.html
And add some initial code for a webpage:
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Vue Template Form Validation</title>
</head>
<body>
<!-- ... -->
</body>
</html>
A browser build for Vue.js is available via cdnjs
. A browser build for VeeValidate is available via jsdelivr
. Add both to the <body>
of the register.html
file:
<body>
<!-- include the Vue.js framework -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js"></script>
<!-- include the VeeValidate library -->
<script src="https://cdn.jsdelivr.net/npm/vee-validate@3.3.8/dist/vee-validate.min.js"></script>
</body>
These files are provided by CDNs (content delivery networks). There is nothing to download or save locally.
You now have a webpage ready to use the latest stable versions (as of this writing) of Vue.js and VeeValidate.
To establish some styling, you can use Bootstrap. To add some iconography, you can also utilize Font Awesome.
Browser builds for Bootstrap and Font Awesome are available via BootstrapCDN
. Add both to the <head>
of the register.html
file:
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Vue Template Form Validation</title>
<!-- include the Bootsrap framework -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<!-- include Font Awesome -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
</head>
At this point, you have Vue, VeeValidate, Bootstrap, and Font Awesome. Next, you will create the form to validate.
This example form will seek five pieces of information from the user. You will need a name
, email
, username
, password
, and password_confirmation
.
First, add some initial markup for the form to the <body>
of the register.html
file before the <script>
tags:
<body>
<div class="container my-3">
<div class="row justify-content-around">
<div class="col-6 rounded shadow">
<h1 class="py-3">Sign up once and watch any of our free demos.</h1>
<div id="signup-form">
<form>
<!-- ... form fields ... -->
</form>
</div>
</div>
</div>
</div>
<!-- ... library script tags ... -->
</body>
This code establishes an empty <form>
and uses some Bootstrap utilities for layout and appearance.
Next, add the form fields to the <form>
. Start with the field for name
:
<form>
<div class="form-group">
<label for="name">Your Name</label>
<div class="input-group">
<span class="input-group-prepend"><span class="input-group-text"><i class="fa fa-user" aria-hidden="true"></i></span></span>
<input type="text" id="name" name="name" placeholder="Name" class="form-control" />
</div>
</div>
</form>
This code creates a <label>
for name
, a Font Awesome icon for fa-user
, and an <input>
for name
.
You can make similar additions to the <form>
for the other pieces of information—email
, username
, password
, and password_confirmation
:
<form>
<!-- ... name ... -->
<div class="form-group">
<label for="email">Your Email</label>
<div class="input-group">
<span class="input-group-prepend"><span class="input-group-text"><i class="fa fa-envelope" aria-hidden="true"></i></span></span>
<input type="email" id="email" name="email" placeholder="email@example.com" class="form-control" />
</div>
</div>
<div class="form-group">
<label for="username">Username</label>
<div class="input-group">
<span class="input-group-prepend"><span class="input-group-text"><i class="fa fa-users" aria-hidden="true"></i></span></span>
<input type="text" id="username" name="username" placeholder="Enter your username" class="form-control" />
</div>
</div>
<div class="form-group">
<label for="password">Password</label>
<div class="input-group">
<span class="input-group-prepend"><span class="input-group-text"><i class="fa fa-lock" aria-hidden="true"></i></span></span>
<input type="password" id="password" name="password" placeholder="Enter a password" class="form-control" />
</div>
</div>
<div class="form-group">
<label for="password_confirmation">Confirm Password</label>
<div class="input-group">
<span class="input-group-prepend"><span class="input-group-text"><i class="fa fa-lock" aria-hidden="true"></i></span></span>
<input type="password" id="password_confirmation" name="password_confirmation" placeholder="Re-type password" class="form-control" />
</div>
</div>
</form>
This code creates <label>
s, a Font Awesome icons, and <input>
s. Each input has a unique id
and name
.
Add a button for registration to complete the <form>
:
<form>
<!-- ... form fields ... -->
<div class="form-group">
<button type="submit" class="btn btn-block btn-lg btn-primary">Register</button>
</div>
</form>
This code creates a large submit button using Bootstrap styles.
You can open register.html
in a web browser to check the progress of the app.
Next, you will create a Vue instance and mount it to the #signup-form
.
Add a new <script>
tag at the end of the <body>
and define signupForm
:
<body>
<!-- ... form ... -->
<!-- ... library script tags ... -->
<script>
const signupForm = new Vue({
el: '#signup-form'
});
</script>
</body>
Add properties to the data
object:
<body>
<!-- ... form ... -->
<!-- ... library script tags ... -->
<script>
const signupForm = new Vue({
el: '#signup-form',
data: {
name: '',
email: '',
username: '',
password: '',
password_confirmation: ''
}
});
</script>
</body>
Then, reference the properties with v-model
in each of the fields.
For the name
field, add the following:
<input type="text" id="name" name="name" placeholder="Name" class="form-control" v-model="name" />
For the email
field, add the following:
<input type="email" id="email" name="email" placeholder="email@example.com" class="form-control" v-model="email" />
For the username
field, add the following:
<input type="text" id="username" name="username" placeholder="Enter your username" class="form-control" v-model="username" />
For the password
field, add the following:
<input type="password" id="password" name="password" placeholder="Enter a password" class="form-control" v-model="password" />
Finally, for the password_confirmation
field, add the following:
<input type="password" id="password_confirmation" name="password_confirmation" placeholder="Re-type password" class="form-control" v-model="password_confirmation" />
At this point, you have a Vue instance with models for name
, email
, username
, password
, and password_confirmation
.
ValidationObserver
and ValdiationProvider
Next, you will need to register ValidationObserver
and ValidationProvider
.
You can add both to a new <script>
tag at then end of the <body>
:
<body>
<!-- ... form ... -->
<!-- ... library script tags ... -->
<script>
Vue.component('validation-observer', VeeValidate.ValidationObserver);
Vue.component('validation-provider', VeeValidate.ValidationProvider);
</script>
<!-- ... vue instance script tag ... -->
</body>
Now, you can use <validation-observer>
to wrap the entire <form>
. And you can use <validation-provider>
to wrap the fields:
<validation-observer>
<form>
<div class="form-group">
<validation-provider>
<label for="name">Your Name</label>
<div class="input-group">
<span class="input-group-prepend"><span class="input-group-text"><i class="fa fa-user" aria-hidden="true"></i></span></span>
<input type="text" id="name" name="name" placeholder="Name" class="form-control" v-model="name" />
</div>
</validation-provider>
</div>
<div class="form-group">
<validation-provider>
<label for="email">Your Email</label>
<div class="input-group">
<span class="input-group-prepend"><span class="input-group-text"><i class="fa fa-envelope" aria-hidden="true"></i></span></span>
<input type="email" id="email" name="email" placeholder="email@example.com" class="form-control" v-model="email" />
</div>
</validation-provider>
</div>
<div class="form-group">
<validation-provider>
<label for="username">Username</label>
<div class="input-group">
<span class="input-group-prepend"><span class="input-group-text"><i class="fa fa-users" aria-hidden="true"></i></span></span>
<input type="text" id="username" name="username" placeholder="Enter your username" class="form-control" v-model="username" />
</div>
</validation-provider>
</div>
<div class="form-group">
<validation-provider>
<label for="password">Password</label>
<div class="input-group">
<span class="input-group-prepend"><span class="input-group-text"><i class="fa fa-lock" aria-hidden="true"></i></span></span>
<input type="password" id="password" name="password" placeholder="Enter a password" class="form-control" v-model="password" />
</div>
</validation-provider>
</div>
<div class="form-group">
<validation-provider>
<label for="password_confirmation">Confirm Password</label>
<div class="input-group">
<span class="input-group-prepend"><span class="input-group-text"><i class="fa fa-lock" aria-hidden="true"></i></span></span>
<input type="password" id="password_confirmation" name="password_confirmation" placeholder="Re-type password" class="form-control" v-model="password_confirmation" />
</div>
</validation-provider>
</div>
<div class="form-group">
<button type="submit" class="btn btn-block btn-lg btn-primary">Register</button>
</div>
</form>
</validation-observer>
You now have a form prepared with <validation-observer>
and <valdiation-provider>
.
A VeeValidate rule sets limits or conditions on what can be entered in one or more fields. Validation rules are checked when you update a record containing fields requiring validation. If the rule is violated, a trappable error occurs.
For example, you can use the required
validator:
<validation-provider rules="required">
You can pass multiple validations separated by a pipe character (|
).
For example, you can use the required
and the email
validators:
<validation-provider rules="required|email">
Alternatively, you can pass an object for more flexibility:
<validation-provider :rules="{ required: true, email: true, regex: /[0-9]+/ }">
Now every time the input changes, the validator will run the list of validations from left to right, populating the errors helper object whenever an input fails validation.
As at the time of writing this tutorial, VeeValidate has 30 rules for form validation with an option of creating your own rules.
Next, you need to import the VeeValidateRules
.
You can add it to a new <script>
tag at then end of the <body>
of in the register.html
file:
<body>
<!-- ... form ... -->
<!-- ... library script tags ... -->
<script src="https://cdn.jsdelivr.net/npm/vee-validate@3.3.8/dist/rules.umd.js"></script>
<!-- ... vue instance tags ... -->
</body>
Then, you can loop over the rules to make them all available:
<body>
<!-- ... form ... -->
<!-- ... library script tags ... -->
<script>
Object.keys(VeeValidateRules).forEach(rule => {
VeeValidate.extend(rule, VeeValidateRules[rule]);
});
</script>
<!-- ... vue instance tags ... -->
</body>
And apply required
rules for all the inputs:
<validation-provider rules="required">
For email
, you will also apply a rule for valid email addresses:
<validation-provider rules="required|email">
For password
you will also apply a rule for a minimum length of 6
characters:
<validation-provider rules="required|min:6">
Now, you have required
, email
, and min
rules for the fields.
For password_confirmation
you will need to match the value of password
to be valid. To accomplish this, you will rely on ValidationObserver
, which allows password_confirmation
to be aware of password
.
Add the vid
to the password
field, so password_confirmed
has a target:
<validation-provider rules="required|min:6" vid="password">
Add the confirmed
rule to the password_confirmation
field, so password_confirmed
compares its value against the value for password
:
<validation-provider rules="required|confirmed:password">
Now, you have required
, email
, min
, and confirmed
rules for the fields.
VeeValidate allows you to write custom validation rules and messages with extend
and validate
.
Add a rule that prevents users from registering with some restricted words. In this example, you will restrict users from using the words admin
, password
, and administrator
:
<body>
<!-- ... form ... -->
<!-- ... library script tags ... -->
<script>
// Declare an array of usernames that are invalid.
const restricted_usernames = [
'admin',
'password',
'administrator'
];
// Extend the custom rule.
VeeValidate.extend('checkuser', {
name: 'Restricted Usernames',
validate: value => {
return restricted_usernames.includes(value.toLowerCase()) ? false : !! value
},
message: 'That {_field_} is unavailable.',
});
</script>
<!-- ... vue instance tags ... -->
</body>
Add the custom rule to the username
field:
<validation-provider rules="required|checkuser">
Now, you have required
, email
, min
, confirmed
, and checkuser
rules for the fields. The rules are all established, and now you can start displaying error messages.
VeeValidate has errors
available. VeeValidate also has multiple flags for state information. You can access these using Vue’s v-slot
.
You will also use Vue’s v-show
to display the VeeValidate error messages and use Bootstrap’s invalid-feedback
class for styling the errors.
Additionally, you will use VeeValidate flags for dirty
, valid
, and invalid
in combination with Vue’s v-bind:class
and Bootstrap’s is-valid
and is-invalid
classes for styling the fields:
<validation-observer>
<form>
<div class="form-group">
<validation-provider rules="required|alpha" v-slot="{ dirty, valid, invalid, errors }">
<label for="name">Your Name</label>
<div class="input-group">
<span class="input-group-prepend"><span class="input-group-text"><i class="fa fa-user" aria-hidden="true"></i></span></span>
<input type="text" id="name" name="name" placeholder="Name" class="form-control" v-model="name" v-bind:class="{ 'is-valid': dirty && valid, 'is-invalid': dirty && invalid }" />
</div>
<div class="invalid-feedback d-inline-block" v-show="errors">{{ errors[0] }}</div>
</validation-provider>
</div>
<div class="form-group">
<validation-provider rules="required|email" v-slot="{ dirty, valid, invalid, errors }">
<label for="email">Your Email</label>
<div class="input-group">
<span class="input-group-prepend"><span class="input-group-text"><i class="fa fa-envelope" aria-hidden="true"></i></span></span>
<input type="email" id="email" name="email" placeholder="email@example.com" class="form-control" v-model="email" v-bind:class="{ 'is-valid': dirty && valid, 'is-invalid': dirty && invalid }" />
</div>
<div class="invalid-feedback d-inline-block" v-show="errors">{{ errors[0] }}</div>
</validation-provider>
</div>
<div class="form-group">
<validation-provider rules="required|checkuser" v-slot="{ dirty, valid, invalid, errors }">
<label for="username">Username</label>
<div class="input-group">
<span class="input-group-prepend"><span class="input-group-text"><i class="fa fa-users" aria-hidden="true"></i></span></span>
<input type="text" id="username" name="username" placeholder="Enter your username" class="form-control" v-model="username" v-bind:class="{ 'is-valid': dirty && valid, 'is-invalid': dirty && invalid }" />
</div>
<div class="invalid-feedback d-inline-block" v-show="errors">{{ errors[0] }}</div>
</validation-provider>
</div>
<div class="form-group">
<validation-provider rules="required|min:6" vid="password" v-slot="{ dirty, valid, invalid, errors }">
<label for="password">Password</label>
<div class="input-group">
<span class="input-group-prepend"><span class="input-group-text"><i class="fa fa-lock" aria-hidden="true"></i></span></span>
<input type="password" id="password" name="password" placeholder="Enter a password" class="form-control" v-model="password" v-bind:class="{ 'is-valid': dirty && valid, 'is-invalid': dirty && invalid }" />
</div>
<div class="invalid-feedback d-inline-block" v-show="errors">{{ errors[0] }}</div>
</validation-provider>
</div>
<div class="form-group">
<validation-provider rules="required|confirmed:password" v-slot="{ dirty, valid, invalid, errors }">
<label for="password_confirmation">Confirm Password</label>
<div class="input-group">
<span class="input-group-prepend"><span class="input-group-text"><i class="fa fa-lock" aria-hidden="true"></i></span></span>
<input type="password" id="password_confirmation" name="password_confirmation" placeholder="Re-type password" class="form-control" v-model="password_confirmation" v-bind:class="{ 'is-valid': dirty && valid, 'is-invalid': dirty && invalid }" />
</div>
<div class="invalid-feedback d-inline-block" v-show="errors">{{ errors[0] }}</div>
</validation-provider>
</div>
<div class="form-group">
<button type="submit" class="btn btn-block btn-lg btn-primary">Register</button>
</div>
</form>
</validation-observer>
At this point, you have access to dirty
, valid
, invalid
, and errors
. You added logic to display error messages as feedback under the associated field. If the field is interacted with and invalid, it will apply Bootstrap’s is-invalid
class. If the field is interacted with and valid, it will apply Bootstrap’s is-valid
class.
In the next step, you will handle submitting the form.
VeeValidate also provides an invalid
flag for ValidationObserver
and a handleSubmit
function. You can access these using Vue’s v-slot
:
<validation-observer v-slot="{ invalid, handleSubmit }">
Use Vue’s event modifiers to capture form submission with @submit.prevent
. You will also use VeeValidate’s handleSubmit
to prevent form submission until all fields are valid:
<form @submit.prevent="handleSubmit(onSubmit)">
This will call onSubmit
which can be defined as a console.log
message:
<script>
const signupForm = new Vue({
el: '#signup-form',
data: {
name: '',
email: '',
username: '',
password: '',
password_confirmation: ''
},
methods: {
onSubmit: function() {
console.log('Form has been submitted!');
}
}
});
</script>
Keep the <button>
in a disabled
state so it will not submit information while any fields are invalid
:
<button type="submit" class="btn btn-block btn-lg btn-primary" v-bind:disabled="invalid">Register</button>
At this point, you can open register.html
in a web browser and interact with the form to test the validations.
In this tutorial, you have demonstrated how to validate form inputs using the template-driven approach. VeeValidate has allowed you to validate form inputs with existing rules, extend new rules, display errors, and handle the form submission.
If you’d like to learn more about Vue.js, check out our Vue.js topic page for exercises and programming projects.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
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!