Creating a custom validator

Writing new validator

A validator has to follow the syntax:
const customValidator = function() {
return {
validate: function(input) {
// input.element presents the field element
// input.elements presents all field elements
// input.field is the field name
// input.value is the field value
// input.options is the validator options
// Has to return an object
return {
// Required property
// valid: true indicates that the field passes your custom validator
// valid: false, otherwise.
valid: ...,
// Optional: include a custom error message
message: ...,
// Optional: include a meta data that can be used by the library later
meta: {
key: value,
...
},
};
},
};
};
The following example illustrates how to develop a simple validator which validates a password. The validator will treat a password as valid, if it satisfies all the conditions below:
In fact, you can add more conditions to make a secure password.
const strongPassword = function () {
return {
validate: function (input) {
const value = input.value;
if (value === '') {
return {
valid: true,
};
}
// Check the password strength
if (value.length < 8) {
return {
valid: false,
};
}
// The password does not contain any uppercase character
if (value === value.toLowerCase()) {
return {
valid: false,
};
}
// The password does not contain any uppercase character
if (value === value.toUpperCase()) {
return {
valid: false,
};
}
// The password does not contain any digit
if (value.search(/[0-9]/) < 0) {
return {
valid: false,
};
}
return {
valid: true,
};
},
};
};

Registering custom validator

There are two ways to reuse a custom validator.
Adding to FormValidation.validators namespace
const strongPassword = function() {
...
};
// Register the validator
FormValidation.validators.checkPassword = strongPassword;
// Use new validator
FormValidation.formValidation(
document.getElementById('demoForm'),
{
fields: {
pwd: {
validators: {
notEmpty: {
message: 'The password is required and cannot be empty'
},
// checkPassword is name of new validator
checkPassword: {
message: 'The password is too weak'
},
}
}
},
}
);
We register a new validator named checkPassword which is implemented by the strongPassword function. Since the validators are distinct by the names, the new validator name (checkPassword, in the example above) has to be different with built-in validators.
Registering custom validator
This approach should be used when the FormValidation namespace doesn't exist. For example, when the library is used with ES6 module.
const strongPassword = function() {
...
};
// Use new validator
FormValidation
.formValidation(
document.getElementById('demoForm'),
{
fields: {
pwd: {
validators: {
notEmpty: {
message: 'The password is required and cannot be empty'
},
// checkPassword is name of new validator
checkPassword: {
message: 'The password is too weak'
},
}
}
},
}
)
// Register the validator
.registerValidator('checkPassword', strongPassword);

Adding custom message

Basically, the custom validator above works fine. It returns false if the password doesn't satisfy any of conditions we define. The limitation here is that the user don't know which condition the password doesn't pass. It informs the same The password is too weak message all the times.
To improve that, we can set a dynamic error message in custom validator:
const customValidator = function() {
return {
validate: function(input) {
// ... Do your logic checking
if (...) {
return {
valid: true, // or false
message: 'The error message',
};
}
return {
valid: false, // or true
message: 'Other error message',
};
},
};
};
Our password checker now indicates the reason for a weak password:
const strongPassword = function () {
return {
validate: function (input) {
const value = input.value;
if (value === '') {
return {
valid: true,
};
}
// Check the password strength
if (value.length < 8) {
return {
valid: false,
message: 'The password must be more than 8 characters long',
};
}
// The password does not contain any uppercase character
if (value === value.toLowerCase()) {
return {
valid: false,
message: 'The password must contain at least one upper case character',
};
}
// The password does not contain any uppercase character
if (value === value.toUpperCase()) {
return {
valid: false,
message: 'The password must contain at least one lower case character',
};
}
// The password does not contain any digit
if (value.search(/[0-9]/) < 0) {
return {
valid: false,
message: 'The password must contain at least one digit',
};
}
return {
valid: true,
};
},
};
};
Now, the form shows exactly condition that we want the password to satisfy.
Adding custom message

Adding meta data

The rules used in example above are too simple and can't cover most popular cases of weak password. For example, it doesn't check the simple password such as 123456, abcdef, .etc.
In this section, we will use the awesome Dropbox's zxcvbn library to build a password strength meter.
For anyone who haven't known about zxcvbn library, it's a password strength estimator inspired by password crackers developed by Dropbox. It can recognize and weighs 30k common passwords. For more information about this library, you can refer to its official page.
It's quite easy to use it to see how strong a given password is:
const result = zxcvbn(password);
// The password strength score. See the following table for available values
result.score;
// Explain why the password is weak. "This is a top-10 common password", for example
result.feedback.warning;
score is an integer number between 0 and 4 that indicates the strength level:
ScoreDescription
0Too guessable: risky password
1Very guessable: protection from throttled online attacks
2Somewhat guessable: protection from unthrottled online attacks
3Safely unguessable: moderate protection from offline slow-hash scenario
4Very unguessable: strong protection from offline slow-hash scenario
Our custom validator will use zxcvbn and include score in its validation result:
const strongPassword = function () {
return {
validate: function (input) {
// input.value is the field value
// input.options are the validator options
const value = input.value;
if (value === '') {
return {
valid: true,
};
}
const result = zxcvbn(value);
const score = result.score;
const message = result.feedback.warning || 'The password is weak';
// By default, the password is treat as invalid if the score is smaller than 3
// We allow user to change this number via options.minimalScore
const minimalScore = input.options && input.options.minimalScore ? input.options.minimalScore : 3;
if (score < minimalScore) {
return {
valid: false,
// Yeah, this will be set as error message
message: message,
meta: {
// This meta data will be used later
score: score,
},
};
}
},
};
};
Next, we will use the score which is inclued the meta data of validation result to show up a password strength meter. Bootstrap's progress is used to simply demo the functionality.
<input type="password" class="form-control" name="pwd" autocomplete="off" />
<!-- The progress bar is hidden initially -->
<div class="progress mt-2" id="progressBar" style="opacity: 0;">
<div class="progress-bar progress-bar-striped progress-bar-animate" style="width: 100%"></div>
</div>
We handle the core.validator.validated event to update the progress bar:
FormValidation
.formValidation(
document.getElementById('demoForm'),
{
fields: {
...
},
plugins: {
...
},
}
)
.registerValidator('checkPassword', strongPassword)
.on('core.validator.validated', function(e) {
if (e.field === 'pwd' && e.validator === 'checkPassword') {
const progressBar = document.getElementById('progressBar');
if (e.result.meta) {
// Get the score which is a number between 0 and 4
const score = e.result.meta.score;
// Update the width of progress bar
const width = (score === 0) ? '1%' : score * 25 + '%';
progressBar.style.opacity = 1;
progressBar.style.width = width;
} else {
progressBar.style.opacity = 0;
progressBar.style.width = '0%';
}
}
});
Lastly, for a full working demonstration, you can play with the following form:
Try to enter simple passwords such as 123456, abcdef, abc123 to see how they are treated
Adding meta data

See also