The f-validation module is used to add client-side validation to a form.
The module aims to use the default HTML5 form validation attrbutes wherever possible and extend this with it's own rules for more specific validation checks.
For example, if an input within a form passed to f-validate
has a type of email
set, the module will apply the email validation rule to that field.
To setup f-validate
, you first need to install the module using Yarn or NPM.
yarn add @justeat/f-validate
Then within your JavaScript, import the module:
import Validate from '@justeat/f-validate';
Once you have imported the module, you can then attach f-validate
to a form. To do this, you can either pass in the name of your form or the form element itself, such that:
// 1. Pass in the name of the form to be validated
new FormValidation('formName');
// 2. Pass in the form element to be validated
const myForm = document.querySelector('.myForm');
new FormValidation(myForm);
Once you have done this, f-validate
will automatically validate the form based on the HTML5 form attributes set on each field (such as required
) or by checking whether a field has the specified rule data-attribute set on it. For more information on how to specify rules, see the Validation Rules section below.
f-validate
aims to use the default HTML5 form validation attrbutes wherever possible. For example, if an input has a type of email
, it will apply the email validation test to it.
The following rules are currently available within the validation module to apply to fields.
Valid field types = input
| select
| textarea
| radio
| checkbox
Default Error Message = 'This field is required.'
This rule checks that a value is present for the field being validated.
To validate a field with this rule, either set the required
HTML5 attribute or add the data-val-required
attribute to the field:
// via required attribute
<input type="text" required />
// via data-val-required attribute
<input type="text" data-val-required />
When set on an input
, select
or textarea
field, a value must be present for this rule to pass validation.
For fields of type checkbox
, this rule checks that the field has been checked
and for fields of type radio
, this rule will validate as true as long as one radio button with the same name
attribute has been selected.
<div class="g g--alignCenter">
<div class="g-col g-span6">
<form name="test-form">
<label>Name:
<input type="text" required>
</label>
<button type="submit" class="o-btn o-btn--primary o-btn--block">Submit</button>
</form>
</div>
</div>
Valid field type = input
Default Error Message = 'This field must contain a valid email address.'
This rule checks that the value of the field being validated is a valid email address.
To validate a field with this rule, set its type such that type="email"
.
<input type="email" />
This rule uses the following regular expression to validate email addresses:
/^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/
<div class="g g--alignCenter">
<div class="g-col g-span6">
<form name="test-form">
<label>Email:
<input type="email">
</label>
<button type="submit" class="o-btn o-btn--primary o-btn--block">Submit</button>
</form>
</div>
</div>
Valid field types = input
| textarea
Default Error Message = 'This field must be at least {0} characters in length.'
This rule checks that the value of the field is of a specified minimum length. Note that it doesn't check if a value has been entered (like the required
rule), simply that if a value has been entered by the user, then it should be at least x
characters in length.
It can be applied to input
and textarea
fields.
To validate a field with this rule, eiher set the minlength
attribute or add the data-val-minlength
attribute to the field:
// via minlength attribute
<input type="text" minlength="5" />
// via data-val-minlength attribute
<input type="text" data-val-minlength="5" />
<div class="g g--alignCenter">
<div class="g-col g-span6">
<form name="test-form">
<label>Name (minimum 5 characters):
<input type="text" minlength="5">
</label>
<button type="submit" class="o-btn o-btn--primary o-btn--block">Submit</button>
</form>
</div>
</div>
Valid field types = input
| textarea
Default Error Message = 'This field must not exceed {0} characters in length.',
This rule checks that the value of the field is no greater than the specified maximum length.
To validate a field with this rule, eiher set the maxlength
attribute or add the data-val-minlength
attribute to the field:
// via maxlength attribute
<input type="text" maxlength="20" />
// via data-val-maxlength attribute
<input type="text" data-val-maxlength="20" />
Note that when using the maxlength
attribute, most modern browsers will stop the user entering more than the number of characters specified.
<div class="g g--alignCenter">
<div class="g-col g-span6">
<form name="test-form">
<label>Name (maximum 5 characters):
<input type="text" maxlength="5">
</label>
<button type="submit" class="o-btn o-btn--primary o-btn--block">Submit</button>
</form>
</div>
</div>
Valid field types = input
| textarea
Default Error Message = 'This field contains a value that isn’t accepted.'
This rule checks that the value of the field matches the regular expression specified. The pattern must match the entire value and not just some subset.
To validate a field with this rule, eiher set the pattern
attribute or add the data-val-regex
attribute to the field:
// via pattern attribute
<input type="text" pattern="[a-z]{1,6}" />
// via data-val-maxlength attribute
<input type="text" data-val-regex="[a-z]{1,6}" />
It's considered best practice to set the title attribute when validating with this rule to describe the pattern to help the user.
<div class="g g--alignCenter">
<div class="g-col g-span6">
<form name="test-form">
<label>Pattern (Valid pattern = a string of 1-6 letters):
<input type="text" pattern="[a-z]{1,6}">
</label>
<button type="submit" class="o-btn o-btn--primary o-btn--block">Submit</button>
</form>
</div>
</div>
Valid field type = input
Default Error Message = 'This field does not match the {0} field.'
This rule checks that the value of the field being validated matches the value of a separate specified field. To be valid it must match the entire value and not just some subset.
To validate a field with this rule, set the data-val-equalto
attribute to the field:
// via data-val-maxlength attribute
<input type="text" data-val-equalto="matchedField" />
// note that a field with the name passed should exist, or the rule will always fail
<input type="text" name="matchedField" />
<div class="g g--alignCenter">
<div class="g-col g-span6">
<form name="test-form">
<ul class="u-unstyled">
<li>
<label>First field
<input type="text" required data-val-equalto="fieldTwo">
</label>
</li>
<li>
<label>Second field
<input type="text" name="fieldTwo">
</label>
</li>
</ul>
<button type="submit" class="o-btn o-btn--primary o-btn--block">Submit</button>
</form>
</div>
</div>
Valid field type = select
Default Error Message = 'This date must be in the future.'
This rule is for validating dates entered by a collection of select
fields. When applied to a group of fields, it returns true if the date entered in these fields is in the future.
Currently, this rule only validates dates specified with a month and year and has not yet been extended to test for day.
To validate a group with this rule, set the data-val-group
and data-val-dateinfuture
attributes on the container wrapping the select
fields.
Then apply the data-val-dateinfuture-type="month"
and data-val-dateinfuture-type="year" attributes to each
select` field, such that:
// Apply `data-val-dateinfuture` attribute to the outer container
<div data-val-group data-val-dateinfuture>
// Apply `data-val-dateinfuture-type` attributes to the relevant `select` fields
<select data-val-dateinfuture-type="year">
<option value="" ></option>
<option value="2018"></option>
</select>
<select data-val-dateinfuture-type="month">
<option value="" ></option>
<option value="01"></option>
</select>
</div>
<div class="g">
<div class="g-col g-span4">
<form name="test-form">
<div data-val-group data-val-dateinfuture>
<select data-val-dateinfuture-type="year">
<option value="" ></option>
<option value="2017">2017</option>
<option value="2018">2018</option>
<option value="2019">2019</option>
<option value="2020">2020</option>
</select>
<select data-val-dateinfuture-type="month">
<option value="" ></option>
<option value="01">01</option>
<option value="02">02</option>
<option value="03">03</option>
<option value="04">04</option>
<option value="05">05</option>
<option value="06">06</option>
<option value="07">07</option>
<option value="08">08</option>
<option value="09">09</option>
<option value="10">10</option>
<option value="11">11</option>
<option value="12">12</option>
</select>
</div>
<button type="submit" class="o-btn o-btn--primary o-btn--block">Submit</button>
</form>
</div>
</div>
Valid field type = input
Default Error Message = 'This field is required.'
This validation rule checks that if a specified checkbox is not checked, then it is required that a value must be entered in the field with this validation check.
This also means that if the specified checkbox is checked, then the field is not required and the form will return valid when the field is empty.
To validate a field with this rule, set the data-val-conditionalrequired
attribute on the field to be validated, passing in the name of the checkbox to validate against:
// via `data-val-conditionalrequired` attribute
<input type="text" data-val-conditionalrequired="nameOfcheckedInput" />
// the checkbox to test against
<input type="checkbox" name="nameOfcheckedInput" />
<div class="g g--alignCenter">
<div class="g-col g-span6">
<form name="test-form">
<ul class="u-unstyled">
<li>
<label>Name:
<input type="text" data-val-conditionalrequired="nameOfcheckedInput" />
</label>
</li>
<li>
<label>If this isn't checked, then the name field is required:
<input type="checkbox" name="nameOfcheckedInput" />
</label>
</li>
</ul>
<button type="submit" class="o-btn o-btn--primary o-btn--block">Submit</button>
</form>
</div>
</div>
Valid field type = All
Default Error Message = 'Custom validation failed.'
This validation rule allows the addition of a custom validation check to be added to the field.
To validate a field with a custom rule, you need to first set the data-val-custom
attribute with the name of the function you want to call when the form is submitted.
You should then ensure that a data-val-custom-error
attribute has been set with your custom error message.
// Apply data attributes to your field
<form name="myForm">
<input data-val-custom="customRule" data-val-custom-error="The value entered does not match 'passTest'" />
</form>
Then in the JavaScript, after you have initialised f-validate
on your form, you should pass in the custom validation function, using the addCustomValidation
method, such that:
const validateForm = new Validate('myForm');
// Pass in a custom validation function
// This function will receive the field being validated
validateForm.addCustomValidation('customRule', field => {
if (field.value === 'passTest') {
return true;
}
return false;
});
<div class="g g--alignCenter">
<div class="g-col g-span6">
<form name="test-form">
<input data-val-custom="customRule" data-custom-error="The value entered does not match 'passTest'" />
<button type="submit" class="o-btn o-btn--primary o-btn--block">Submit</button>
</form>
</div>
</div>
Rule | Fields | Hooks |
---|---|---|
Required |
|
required | data-val-required |
Email |
|
type="email" |
Minlength |
|
minlength | data-val-minlength |
Maxlength |
|
maxlength | data-val-maxlength |
Pattern |
|
pattern | data-val-regex |
Matches |
|
data-val-equalto |
Date In Future |
|
Multiple (see example) |
Conditional Required |
|
data-val-conditionalRequired |
It's common to want to customise the messages displayed when certain fields aren't valid. To do this, you can define a data attribute specific to the validation rule you want the message to apply for, such that data-val-{validation-rule}-error
.
For example:
// To replace the default error message when using the `required` validation rule
// set `data-val-required-error` to the message you would like to display
<input type="text" required data-val-required-error="This is my new required error message" />
// Similarly for other validation rules
// email:
<input type="email" data-val-email-error="This is my new email error message" />
// minlength:
<input type="text" minlength="5" data-val-minlength-error="This is my new minlength error message" />
// maxlength:
<input type="text" maxlength="5" data-val-maxlength-error="This is my new maxlength error message" />
// pattern:
<input type="text" pattern="[a-z]{1,6}" data-val-pattern-error="This is my new pattern error message" />
// matches:
<input type="text" matches="fieldToMatch" data-val-matches-error="This is my new matches error message" />
// conditionalrequired:
<input type="text" data-val-conditionalrequired="nameOfcheckedInput" data-val-conditionalrequired-error="This is my new matches error message" />
// for dateInFuture, apply the `data-val-dateinfuture` to the outer container
<div data-val-group data-val-dateinfuture data-val-dateinfuture-error="This is my new dateInFuture error message">
<div class="g g--alignCenter">
<div class="g-col g-span6">
<form name="test-form">
<ul class="u-unstyled">
<li>
<label>Name:
<input type="text" data-val-required data-val-required-error="This is a custom required error message" />
</label>
</li>
</ul>
<button type="submit" class="o-btn o-btn--primary o-btn--block">Submit</button>
</form>
</div>
</div>
f-validate
has a number of options that allow you to customise its functionality.
The properties or attributes that can be defined are as follows:
Setting | Type | Values | Description |
---|---|---|---|
errorClass |
string | String | has-error (default) |
Defines the class applied to each field when they fail validation. By default, the class added is `has-error`. |
successClass |
string | String | has-success (default) |
Defines the class applied to each field when they pass validation. By default, the class added is `has-success`. |
groupErrorPlacement |
string | bottom | top | Selector |
When set, error messages will be grouped together rather than placed adjacent to each field. If this value is set to `bottom`, then the error messages will be grouped at the bottom of the form. If this value is set to `top`, then the error messages will be grouped at the top of the form. If this value is set as a selector such as `.myClassName` or `[type="submit"]`, then the error messages will be grouped above the element matched in that selector. If this element isn't found, then it will group error messages as if the `top` value had been specified. |
enableHTML5Validation |
boolean | true | false (default) |
Sets whether the `novalidate` attribute is added to the form being validated. By default, `novalidate` is added to a form that is being validated to supress native HTML5 validation. By setting this option to `true` this attribute is not set and native HTML5 validation will not be supressed. |
hybridMode |
boolean | true | false (default) |
When set, both blur (focus lost) and keydown events are bound to each applicable input field. Validation won't run until the user has blurred the input at least once, giving them a chance to enter a valid value before seeing an error. |
Sometimes, rather than display one error per field, you may want to display all of the forms validation messages together as a group.
To do this, you will need to specify the groupErrorPlacement
option when you initialise f-validate
.
new FormValidation(nameOfForm, {
groupErrorPlacement: 'bottom'
});
To see all possible options that groupErrorPlacement
accepts, check the options section of the documentation.
<div class="g g--alignCenter">
<div class="g-col g-span6">
<form name="test-form-group">
<ul class="u-unstyled">
<li>
<label>Name:
<input type="text" data-val-required data-val-required-error="This is a custom required error message" />
</label>
</li>
<li>
<label>Email:
<input type="email" data-val-email-error="This is a custom email error message" />
</label>
</li>
<li>
<label>Postcode:
<input type="text" pattern="[a-z]{0-6}" data-val-pattern-error="This is a custom pattern error message" />
</label>
</li>
</ul>
<button type="submit" class="o-btn o-btn--primary o-btn--block">Submit</button>
</form>
</div>
</div>
To apply multiple rules to the same field, you simply need to add multiple attributes to define the rules you want to validate.
// To make a field validate as required and of type email
<input type="email" required />
// To make a field validate as minlength and maxlength
<input type="text"
data-val-minlength
data-val-maxlength
data-val-minlength-error="Custom error minlength"
data-val-maxlength-error="Custom error maxlength" />
<div class="g g--alignCenter">
<div class="g-col g-span6">
<form name="test-form">
<ul class="u-unstyled">
<li>
<label>Name:
<input type="text"
data-val-minlength="2"
data-val-maxlength="5"
data-val-minlength-error="Name should be at least 2 characters long"
data-val-maxlength-error="Name should be a maximum of 5 characters long" />
</label>
</li>
<li>
<label>Email:
<input type="email" required />
</label>
</li>
</ul>
<button type="submit" class="o-btn o-btn--primary o-btn--block">Submit</button>
</form>
</div>
</div>
f-validate
has a number of events you can hook into. These can be setup by proving a callback function in the options object. Examples of this are show below:
new FormValidation(nameOfForm, {
onSuccess: function() {
console.log("Great success!");
},
onError: function() {
console.log("Form validation failed");
},
onElementError: function(element, validationType) {
console.log(`The ${element.name} element failed the ${validationType} validator`);
}
});
Event | Description | Parameters |
---|---|---|
onSuccess |
Fired after the entire form is validated and is considered valid. | None |
onError |
Fired after the entire form is validated and considered invalid. | None |
onElementError |
Fired after an individual form element fails a validation check |
|