@stormid/validate
v1.0.0-alpha.34
Published
Client-side form validation
Downloads
811
Readme
Validate
Client-side form validation library to support .NET validation using data-val attributes as a drop-in replacement for jQuery validate, and HTML5 attribute-based constraint validation.
Contents
Usage
Install the package
npm i -S @stormid/validate
Import the module
import validate from '@stormid/validate';
Initialise the module via selector string
const [ validator ] = validate('form:not([novalidate])');
Initialise with a DOM element
const element = document.querySelector('form:not([novalidate])');
const [ validator ] = validate(element);
Initialise with a Node list
const elements = document.querySelectorAll('form:not([novalidate])');
const [ validator ] = validate(elements);
Initialise with an Array of elements
const elements = [].slice.call(document.querySelectorAll('form:not([novalidate])'));
const [ validator ] = validate(elements);
Validators
This library supports HTML5 attribute constraints and the data-val attributes generated by .Net Core model validation, .Net MVC DataAnnotation, or .Net Fluent validation libraries.
Multiple validators can be used on a single field. Custom validators can be added via the addMethod API.
Required
The field must have a value. Checkboxes are treated as non-nullable in required validation.
HTML5
<input name="field" id="field" required>
Data attributes
<input name="field" id="field" data-val="true" data-val-required="'field' is required">
Value is matched against the regular expression
/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
which is equivalent to the browser algorithm used in the constraint validation API for type="email", and is intentionally loose due to known issues related to international domain names and the validation of e-mail addresses in HTML. Stricter or more specific validation rules can be specified with a pattern attribute (or data-val-regex).
HTML5
<input type="email" name="field" id="field">
Data attributes
<input name="field" id="field" data-val="true" data-val-email="'field' must be a valid email address">
Url
The value is matched against this regular expresion by Diego Perini
/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i
Stricter or more specific validation rules can be specified with a pattern attribute (or data-val-regex).
HTML5
<input type="url" name="field" id="field">
Data attributes
<input name="field" id="field" data-val="true" data-val-url="'field' must be a valid url">
Pattern/Regex
Value matches the supplied pattern or regular expression
HTML5
<input name="field" id="field" pattern="^http(s)?">
Data attributes
<input name="field" id="field" data-val="true" data-val-regex="'field' must start with http or https" data-val-regex-pattern="^http(s)?">
Digits
Value must contain only characters in the range 0-9
Data attributes
<input name="field" id="field" data-val="true" data-val-digits="'field' must be a number">
Number
Value is matched against the regular expresion
/^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/;
Allowing positive and negative numbers, decimals, and comma separated thousands.
HTML5
<input type="number" name="field" id="field">
Data attributes
<input name="field" id="field" data-val="true" data-val-number="'field' must be a number">
Min
Value is a Number greater or equal to min
HTML5
<input type="number" name="field" id="field" min="0">
Data attributes
<input type="number" name="field" id="field" data-val="true" data-val-min="'field' must be >= 0" data-val-min-min="0">
Max
Value is a Number less than or equal to max
HTML5
<input type="number" name="field" id="field" max="100">
Data attributes
<input type="number" name="field" id="field" data-val="true" data-val-max="'field' must be <= 100" data-val-max-max="100">
Range
Value is a Number within the specified min and max
Data attributes
<input type="number" name="field" id="field" data-val="true" data-val-range="'field' must be between 0 and 100" data-val-range-min="0" data-val-range-max="100">
Length
Value is a String with a length greater than or equal to min and/or a length less than or equal to max
Data attributes
<input name="field" id="field" data-val="true" data-val-length="'field' length must be between 2 and 4" data-val-length-min="2" data-val-length-max="4">
Stringlength
Value is a String with a length less than or equal to max. Equivalent to the length/max validator, generated by .Net stringlength data notation.
Data attributes
<input name="field" id="field" data-val="true" data-val-stringlength="'field' length must be less than 4" data-val-stringlength-max="4">
Maxlength
Value is a String with a length less than or equal to max. Equivalent to the length/max validator, generated by .Net maxlength data notation.
.HTML5
<input name="field" id="field" maxlength="4">
Data attributes
<input name="field" id="field" data-val="true" data-val-maxlength="'field' length must be less than 4" data-val-maxlength-max="4">
Minlength
Value is a String with a length greater than or equal to min. Equivalent to the length/min validator, generated by .Net minlength data notation.
.HTML5
<input name="field" id="field" minlength="2">
Data attributes
<input name="field" id="field" data-val="true" data-val-minlength="'field' length must be greater than 2" data-val-minlength-min="2">
DateISO
Value is a string in a format matching the date ISO standard, (YYYY-MM-DD), matching the regular expression
/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/
Data attributes
<input name="field" id="field" data-val="true" data-val-dateISO="'field' must in the format YYYY-MM-DD">
Equalto
Value must match the value of another field (or multiple fields). The data-val-equalto-other attribute is the name of another field, or a comma-separated list of names of multiple fields.
Data attributes
<input name="password" id="password">
...
<input name="passwordConfirmation" id="passwordConfirmation" data-val="true" data-val-equalto="'password confirmation' must match 'password'" data-val-equalto-other="password">
Remote
Value is validated against via XHR against a remote resource. The resouce is defined by the data-val-remote-url
attribute.
Defaults to a POST request, data-val-remote-type
can be set to make GET requests.
The values of additional fields can be sent in the request by specifying a data-val-remote-additionalfields
attribute, a comma separated list of field ids to be included.
Data attributes
<input name="field" id="field" data-val="true" data-val-remote="'field' must pass remote validation" data-val-remote-url="/api/validate">
Errors
Error message container
The element to contain server-side error messages generated by .Net fluent and unobtrustive validation libraries is recycled by this library, it can also be manually added to HTML when you need more control over the position and markup of the error message container. The data-valmsg-for
provides the association between error message and field/field group name, the id is used by the library to associate the error with the input using aria-describedby.
<span id="field-error-message" class="field-validation-valid" data-valmsg-for="field" />
If this element is not present a span is appended to the label for the field with the className .error-message
.
Error messages
.Net error messages are extracted from data-val-[validator-type] data attributes, and apply to both HTML5 and .Net validators.
Fields without data-val error messages will show the default messages for the failed validator (see options below).
Including values in error messages
To include the user input value in your error message, place a token "{{value}}" within the message string and the script will replace it at the time of validation. e.g. "{{value}} is not a valid email address" will become "test@test is not a valid email address".
If a validation group contains more than one field, the values of these will be returned as a comma seperated list within the message. For example: "{{value}} are not valid inputs" becomes "test1, test2 are not valid inputs".
Options
{
preSubmitHook: false, //function, called on validation pass, before submit
submit: form.submit, // function, to support async form submissions, pass your own submit function
messages: { //default HTML5 error messages
required() { return 'This field is required'; } ,
email() { return 'Please enter a valid email address'; },
pattern() { return 'The value must match the pattern'; },
url(){ return 'Please enter a valid URL'; },
number() { return 'Please enter a valid number'; },
digits() { return 'Please enter only digits'; },
maxlength(props) { return `Please enter no more than ${props.max} characters`; },
minlength(props) { return `Please enter at least ${props.min} characters`; },
max(props){ return `Please enter a value less than or equal to ${props.max}`; },
min(props){ return `Please enter a value greater than or equal to ${props.min}`}
}
}
API
validate() returns an array of instances. Each instance exposes the interface
{
validate
addMethod
validateGroup
addGroup
removeGroup
}
addMethod
Add a custom validation method to a group:
const [ validator ] = validate('.my-form');
validator.addMethod(
'MyFieldName', //input/input group name/or validation group name if passing an array of fields
(value, fields) => { //validation method
//value is the value of the whole group of fields (grouped under the name attribute)
return value === 'test'; //must return boolean
},
'Value must equal "test"', //error message on validation failure
fields // an optional array of inputs, if this isn't present the input/groupName is used as a name (or data-group) attribute selector
);
validate
Manually trigger validation on the whole form, returns a promise:
const [ validator ] = validate('.my-form');
await validator.validate();
addGroup
Add a field or field group to the validator:
const [ validator ] = validate('.my-form');
const fieldsArray = Array.from(document.querySelector('.new-fields'))
//add by passing an array of fields
//if these fields span multiple groups they will be collected into the correct validation groups internally by the validator
validator.addGroup(fieldsArray);
validateGroup
Immediately validates an individual group within the form:
const [ validator ] = validate('.my-form');
const validator.validateGroup('myInput');
//pass in the name or data-val-group value that corresponds to the group you're looking to validate
//returns a promise which resolves with the validity state of the group (true if valid, false if invalid)
removeGroup
Remove a validation group from the validator:
const [ validator ] = validate('.my-form');
const fieldsArray = Array.from(document.querySelectorAll([name=new-fields]))
//add by passing an array of fields
validator.addGroup(fieldsArray);
//remove by passing the name of a group
validator.removeGroup('new-fields');
Plugins
Plugins are a set of pre-built custom validators that are included in the package but not in the default build. They can be imported and used in addMethod you would your own custom validation method.
isValidDate
Validate three separate day/month/year fields (similar to the govuk design system date component) as a single valid date.
The minimum accepted year value in the isValidDate plugin is 1000. To set a different (more recent) minimum value consider using the min validator on the year input.
isFutureDate
Validate three separate day/month/year fields (similar to the govuk design system date component) as a single date in the future.
isPastDate
Validate three separate day/month/year fields (similar to the govuk design system date component) as a single date in the past- today's date is valid.
HTML
<fieldset>
<legend>
<span>Date</span>
<span data-valmsg-for="date" id="date-error-message"></span>
<span data-valmsg-for="dateDay" id="date-Day-error-message"></span>
<span data-valmsg-for="dateMonth" id="date-Month-error-message"></span>
<span data-valmsg-for="dateYear" id="date-Year-error-message"></span>
</legend>
<div class="flex">
<input id="dateDay" name="dateDay" inputmode="numeric" data-val="true" data-val-required="Enter a day" aria-required="true"/>
<input id="dateMonth" name="dateMonth" inputmode="numeric" data-val="true" data-val-required="Enter a month" aria-required="true"/>
<input id="dateYear" name="dateYear" inputmode="numeric" data-val="true" data-val-required="Enter a year" aria-required="true" />
</div>
</fieldset>
JS
import validate from '@stormid/validate';
import { isValidDate } from '@stormid/validate/src/lib/plugins/methods/date';
const [ validator ] = validate('.my-form');
validator.addMethod(
'date', //name of custom validation group
isValidDate, // date validation method imported from the library
'Enter a valid date', // error message
[ document.getElementById('dateDay'), document.getElementById('dateMonth'), document.getElementById('dateYear') ] //date fields array [day, month, year]
);
Tests
npm t
License
MIT