https://github.com/friendsofecmascript/validatory
A minimal yet powerful form validation library written in modern JavaScript. Zero dependencies!
https://github.com/friendsofecmascript/validatory
form form-validation javascript library validation
Last synced: 7 days ago
JSON representation
A minimal yet powerful form validation library written in modern JavaScript. Zero dependencies!
- Host: GitHub
- URL: https://github.com/friendsofecmascript/validatory
- Owner: FriendsOfECMAScript
- License: mit
- Created: 2017-11-29T16:18:58.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2019-01-02T09:56:55.000Z (over 7 years ago)
- Last Synced: 2025-11-14T01:03:00.608Z (7 months ago)
- Topics: form, form-validation, javascript, library, validation
- Language: JavaScript
- Homepage:
- Size: 107 KB
- Stars: 1
- Watchers: 3
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# Validatory
> A minimal yet powerful form validation library written in modern JavaScript. Zero dependencies!
## Installation
The recommended and the most suitable way to install it is using *Yarn*:
```bash
$ yarn add validatory
```
or alternatively with *NPM*:
```bash
$ npm install --save validatory
```
## How does it work?
The library will validate your *form-elements* using by default a ***RegExp* pattern based validation**, setting the
corresponding `data-validation-state` attribute both to the *form-element* and the associated *form* after each
*form-element*'s **input** and **change** events.
The *form-elements* will be the ones that are queried with the initialization's `formElementSelector` parameter, and the
`data-validation-state` will be added by default to the *form-element*'s root node.
The *form-element* will have the following states:
- **data-validation-state="not-validated"** - This is the initial validation state.
- **data-validation-state="not-filled"** - If the element is marked as *required* but hasn't any value yet.
- **data-validation-state="valid"** - The element's value is valid.
- **data-validation-state="not-valid"** - If the element value hasn't passed the validation process. (Generic state)
- **data-validation-state="not-valid-custom-state"** - If the element value hasn't passed the validation process and the `custom-state` has been defined.
The *form* will have the following states:
- **data-validation-state="not-validated"** - This is the initial validation state.
- **data-validation-state="not-valid"** - If any of it's *form-elements* state is not valid.
- **data-validation-state="valid"** - If all of it's *form-elements* state is valid.
For example, after the validation has been triggered, a non-valid form element will have the following state:
```html
```
## Features
* **Asynchronous validation**
* Validate **required**
* Validate values with **regular expressions**
* **Built-in validation patterns**: 9 digit phone number, Email, ...
* Write your own **custom validator**
## Usage
In order to work with the library your DOM must, at least, have the following *data attributes* properly set.
- **data-validate**
- **required**
- **data-validate-phone**
- **data-validate-email**
- **data-validate-any**
- **data-validate-pattern="your-pattern"**
- **data-validation-state-reference-selector="your-selector"**
### DOM attributes
#### form
If you want to validate a **\**, you must include the `data-validate` attribute. It's validation state will
depend on it's elements' validation states.
```html
```
#### form-element
If you want to validate an **\**, **\**, **\**, ... , you must as well include the
`data-validate` attribute. It's validation will be triggered after each `input` and `change` events.
```html
```
#### form-element - required
By setting the `required` attribute, the form element's value will be required, and it's state won't be valid until
it's filled..
```html
```
#### form-element - built-in validators
As the validation process is by default based on *RegExp* patterns, the library will provide you the most common
value-type-associated patterns. For the time being, it will automatically add the corresponding `data-validate-pattern`
attribute to the elements with the following `data-validate-`type attributes.
Built-in validators won't work if the form element has not included the `data-validate` attribute.
##### *data-validate-phone*
It will validate **9 digit phone numbers**. It will add the corresponding `data-validate-pattern` attribute to the
elements with the `data-validate-phone` attribute on runtime.
```html
```
Both *123-123-123* and *123123123* phones will be valid.
##### *data-validate-email*
It will validate the element's value as an **email**, adding the correponding `data-validate-pattern` attribute to the
elements with the `data-validate-email` attribute.
```html
```
##### *data-validate-any*
This built-in validator will validate **any value** as valid.
```html
```
#### form-element - custom pattern validation
Apart from the built-in validators, you may want to validate a form element using a custom *RegExp* pattern. By adding a
`data-validate-pattern="(-your RegExp pattern-)"` attribute to an element with the `data-validate` attribute, the
library will automatically use the provided pattern for validating the associated value. For example:
###### Any value but *aValue*
This pattern will test as valid any value but the provided one.
```html
```
###### 2-5 letter palindrome
This pattern will test as valid any value but the provided one.
```html
```
And so on.
#### Custom *data-validation-state* dom node
If you want the associated `data-validation-state` attribute to be set o node different from the selector matching one,
you will want to use the `data-validation-state-reference-selector` attribute. The library will look up for the first
selector-matching parent. For example:
```html
I have read an accept the terms and conditions
```
### Initialization - basic
In order to properly initialize the library, we will call the library's `init` method, passing at least the following
parameters.
```js
import {init} from 'validatory';
init({
formSelector: 'form',
formElementSelector: 'input, select, textarea'
});
```
Both *formSelector* and *formElementSelector* can be any valid CSS selectors.
### Initialization - with callbacks
Initialization with callbacks.
```js
import {init} from 'validatory';
const
formValidationStateChangedCallback = formValidatorInstance => {
const formState = formValidatorInstance.state;
// ...
},
formElementValidationStateChangedCallback = formElementValidatorInstance => {
const formElementState = formElementValidatorInstance.state;
// ...
};
init({
formSelector: 'form',
formElementSelector: 'input, select, textarea',
onFormValidationStateChanged: formValidationStateChangedCallback,
onFormElementValidationStateChanged: formElementValidationStateChangedCallback
});
```
### Styling
As the library will set a **data-validation-state** attribute, we will style the element using this *data attribute*.
For example:
```scss
.form-input {
&[data-validation-state="not-filled"],
&[data-validation-state="not-valid"] {
border-color: $form-input-border-color-error;
}
&[data-validation-state="valid"] {
border-color: $form-input-border-color-valid;
}
}
```
### Displaying error messages
We will use the **data-validation-state** attribute to show/hide the associated error messages. For example:
```html
*Phone
This field is required.
Entered phone does not match the required pattern.
```
```scss
.form-group-input__errors {
display: none;
position: relative;
z-index: -1;
}
[data-validation-state="not-valid"] + .form-group-input__errors,
[data-validation-state="not-filled"] + .form-group-input__errors {
display: block;
}
[data-validation-state="not-valid"] + .form-group-input__errors {
.form-error--not-valid {
animation: $form-group-errors-animation;
display: block;
}
}
[data-validation-state="not-filled"] + .form-group-input__errors {
.form-error--not-filled {
animation: $form-group-errors-animation;
display: block;
}
}
```
### Extending the library's Validators
In order to extend the library's validators, we will add a new **Validator** instance to the *ValidatorRegistry*,
passing the required constructor parameters.
Your custom validation implementation must return/resolve with an object shaped as follows:
```js
{
valid: boolean,
errorCode: string (optional)
}
```
For example:
#### Synchronous validator
```js
import {validatorRegistry, Validator} from 'validatory';
const customValidator = new Validator({
supports: node => node.classList.contains('form-textarea'),
isEmpty: node => node.value === '',
isValid: node => ({
valid: node.value === 'test'
}),
});
validatorRegistry.add(customValidator);
```
#### Asynchronous validator
```js
import {validatorRegistry, Validator} from 'validatory';
const yourAsyncValidationPromise = new Promise(resolve => setTimeout(() => resolve(true), 3000));
const customValidator = new Validator({
supports: node => node.classList.contains('form-textarea'),
isEmpty: node => node.value === '',
isValid: node => new Promise(resolve => yourAsyncValidationPromise.then(result => {
const valid = result === 'valid'; // Your custom validation
return resolve(valid ? {valid} : {valid, errorCode: 'your-custom-error-code'});
})),
});
validatorRegistry.add(customValidator);
```
### Extending the library's Validators - Async validation helper
The library provides a helper method `asyncValidation` that will wrap your async logic and reduce the boilerplate needed to write a custom async validator.
```js
import {asyncValidation} from 'validatory';
const isValid = node => asyncValidation(fetch('https://jsonplaceholder.typicode.com/posts/1'), response =>
node.value === 'bla bla' ? {valid: true} : {valid: false, errorCode: 'no-service'});
const customAsyncValidator = new Validator({
supports: node => node.id === 'asyn-custom-validation-field',
isEmpty: node => node.value === '',
isValid: node => isValid(node)
});
````
### Extending the library's Validators - Full example with Styles & custom `data-states`
If your are writting a custom validator that needs to display a custom error message based on the validation logic,
you will need to implement the validator and add these styles to your app.
In this example, we are using the `es6-promise-debounce` for debouncing the validation process.
```js
import debounce from 'es6-promise-debounce';
import {validatorRegistry, Validator, asyncValidation} from 'validatory';
const
debouncedValidation = debounce(node => {
console.log('Asynchronous validation started');
const validZipCode = /^\d{5}$/.test(node.value); // zip code format validation
if (!validZipCode) {
return {valid: false, errorCode: 'zip-code'};
}
return asyncValidation(fetch('https://jsonplaceholder.typicode.com/posts/1'), response => {
const valid = node.value === '01005';
return valid ? {valid} : {valid: false, errorCode: 'no-service'};
});
}, 500),
asyncValidator = new Validator({
supports: node => node.id === 'async',
isEmpty: node => node.value === '',
isValid: node => debouncedValidation(node),
});
validatorRegistry.add(asyncValidator);
```
```scss
@import './../_definitions/animations';
$form-group-errors-animation: $animation-vertical-node-in;
[data-validation-state="not-valid-zip-code"] ~ .form-group-input__errors .form-error--not-valid-zip-code,
[data-validation-state="not-valid-no-service"] ~ .form-group-input__errors .form-error--not-valid-no-service {
animation: $form-group-errors-animation;
display: block;
}
```
For a full working example, checkout the [tests/app][1] demo.
[1]: https://github.com/FriendsOfECMAScript/Validatory/tree/master/tests/app