Merge pull request #186 from hardikpthv/fix/custom-validator-docs
Update custom validator docs
This commit is contained in:
commit
f4ef3a4a11
1 changed files with 81 additions and 77 deletions
|
|
@ -2,63 +2,83 @@
|
||||||
|
|
||||||
Validators consist of an array in the format of `[<function>, <?params>, <?opts>]`.
|
Validators consist of an array in the format of `[<function>, <?params>, <?opts>]`.
|
||||||
They are implemented via pure functions and can be coupled to localized messages.
|
They are implemented via pure functions and can be coupled to localized messages.
|
||||||
They should be used in conjunction with a [`Form Control`](../../../field/docs/FormFundaments.md) that implements
|
They should be used in conjunction with a [`Form Control`](../../../field/docs/FormFundaments.md) that implements the [`ValidateMixin`](../../).
|
||||||
the [`ValidateMixin`](../../).
|
|
||||||
|
|
||||||
This tutorial will show you how to build your own custom iban validator, including a factory
|
This tutorial will show you how to build your own custom IBAN validator, including a validator factory function that makes it easy to apply and share it.
|
||||||
function that makes it easier to apply and share it.
|
|
||||||
|
|
||||||
## Adding a validation method
|
## Implement the validation logic
|
||||||
|
|
||||||
It's a good habit to make your Validator compatible with as many locales and languages as possible.
|
It's a good habit to make your validators compatible with as many locales and languages as possible.
|
||||||
For simplicity, this tutorial will only support Dutch and US zip codes.
|
For simplicity, this tutorial will only support Dutch and US zip codes.
|
||||||
|
|
||||||
As a best practice, create your validator in a new file from which it can be exported for eventual
|
As a best practice, create your validator in a new file from which it can be exported for eventual reuse.
|
||||||
reuse.
|
|
||||||
As can be read in [`Validate`](../ValidationSystem.md), a validator applied to
|
|
||||||
`.errorValidators` expects an array with a function, a parameters object and an additional
|
|
||||||
configuration object.
|
|
||||||
|
|
||||||
First we will define the most important part, the function. We will start with the function body:
|
We will start with this simple function body:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
export function ibanValidateFunction() {
|
export function validateIban() {
|
||||||
return {
|
return true;
|
||||||
isIban : true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
As you can see, the validator returns an object with the name, followed by an expression
|
It return a boolean, which is `true` if the value is valid.
|
||||||
determining its validity.
|
|
||||||
|
Which value? The [modelValue](../../../field/docs/FormattingAndParsing.md) like this:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
export function ibanValidateFunction(modelValue) {
|
export function validateIban(modelValue) {
|
||||||
return {
|
return /\d{5}([ \-]\d{4})?$/.test(modelValue.trim());
|
||||||
isIban : /\d{5}([ \-]\d{4})?$/.test(modelValue.trim());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The above function validates ibans for the United States ('us').
|
The above function validates IBANs for the United States ('us').
|
||||||
As you can see, the first parameter of a validator is always the
|
|
||||||
[`modelValue`](../../../field/docs/FormattingAndParsing.md). This is the value that our validity should depend upon.
|
Suppose we want to support 'nl' IBANs as well.
|
||||||
Suppose we want to support 'nl' ibans as well. Since our validator is a pure function,
|
Since our validator is a pure function, we need a parameter for it:
|
||||||
we need a parameter for it:
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
export function ibanValidateFunction(modelValue, { country } = {}) {
|
export function validateIban(modelValue, { country } = {}) {
|
||||||
const regexes = {
|
const regexpes = {
|
||||||
'us' : /\d{5}([ \-]\d{4})?$/,
|
us: /\d{5}([ \-]\d{4})?$/,
|
||||||
'nl' : /^[1-9]{1}[0-9]{3} ?[a-zA-Z]{2}$/,
|
nl: /^[1-9]{1}[0-9]{3} ?[a-zA-Z]{2}$/,
|
||||||
}
|
};
|
||||||
|
|
||||||
return {
|
return regexpes[country || 'us'].test(modelValue.trim());
|
||||||
isIban : regexes(country || 'us').test(modelValue.trim());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Creating a validate function
|
||||||
|
|
||||||
|
As can be read in [validate](../ValidationSystem.md), a validator applied to `.errorValidators` expects an array with a validate function, a parameters object and an additional configuration object.
|
||||||
|
|
||||||
|
It expects a function with such an interface:
|
||||||
|
|
||||||
|
```js
|
||||||
|
export function isIban(...args) {
|
||||||
|
return {
|
||||||
|
'my-app-isIban': validateIban(...args),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`my-app-isIban` is the unique id of the validator.
|
||||||
|
The naming convention for this unique id will be explained later in this document.
|
||||||
|
|
||||||
|
## Creating a reusable factory function
|
||||||
|
|
||||||
|
But this is way easier to create a factory function which will automatically create an array for `.errorValidators`.
|
||||||
|
We recommend using this approach and from now on when saying a validator function we mean this one:
|
||||||
|
|
||||||
|
```js
|
||||||
|
export const isIbanValidator = (...factoryParams) => [
|
||||||
|
(...params) => ({ 'my-app-isIban': isIban(...params) }),
|
||||||
|
...factoryParams,
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
Here the naming convention is `validator name` + `Validator`.
|
||||||
|
Thus, `isIbanValidator` is your validator factory function.
|
||||||
|
|
||||||
## Adding an error message
|
## Adding an error message
|
||||||
|
|
||||||
<!-- TODO: increase DX here and probably deprecate this approach.
|
<!-- TODO: increase DX here and probably deprecate this approach.
|
||||||
|
|
@ -68,72 +88,56 @@ in prebaked factory functions. There's no need for a global namespace then.
|
||||||
On top/better: consider a less coupled design (localization outside of validation and strings being passed
|
On top/better: consider a less coupled design (localization outside of validation and strings being passed
|
||||||
to feedback renderer) -->
|
to feedback renderer) -->
|
||||||
|
|
||||||
Now, we want to add an error message. For this, we need to have a bit more knowledge about how the
|
Now, we want to add an error message.
|
||||||
ValidateMixin handles translation resources.
|
For this, we need to have a bit more knowledge about how the `ValidateMixin` handles translation resources.
|
||||||
|
|
||||||
As can be read in [Validate](../../), the `ValidateMixin` considers all namespaces
|
As can be read in [validate](../../), the `ValidateMixin` considers all namespaces configured via `get loadNamespaces`.
|
||||||
configured via `get loadNamespaces`. By default, this contains at least the `lion-validate`
|
By default, this contains at least the `lion-validate` namespace which is added by the `ValidateMixin`.
|
||||||
namespace which is added by the `ValidateMixin`. On top of this, for every namespace found, it adds
|
On top of this, for every namespace found, it adds an extra `{namespace}-{validatorUniqueId}` namespace.
|
||||||
an extra `{namespace}-{validatorName}` namespace.
|
Let's assume we apply our validator on a regular `<lion-input>`.
|
||||||
Let's assume we apply our validator on a regular `<lion-input>`. That would mean on validation these
|
If our `validatorUniqueId` was `isIban`, that would mean on validation these two namespaces are considered, and in this order:
|
||||||
two namespaces are considered, and in this order:
|
|
||||||
|
|
||||||
* lion-validate+isIban
|
- lion-validate+isIban
|
||||||
* lion-validate
|
- lion-validate
|
||||||
|
|
||||||
One should be aware that localize namespaces are defined in a global scope, therefore the approach
|
One should be aware that localize namespaces are defined in a global scope.
|
||||||
above would only work fine when the iban validator would be part of the core code base (lion-web).
|
Therefore the approach above would only work fine when the IBAN validator would be part of the core code base ([validate](../../)).
|
||||||
|
|
||||||
As long as validators are part of an application, we need to avoid global namespace clashes.
|
As long as validators are part of an application, we need to avoid global namespace clashes.
|
||||||
Therefore, we recommend to prefix the application name, like this:
|
Therefore, we recommend to prefix the application name, like this: `my-app-isIban`.
|
||||||
|
|
||||||
```js
|
The resulting `lion-validate+my-app-isIban` namespace is now guaranteed to be unique.
|
||||||
export function ibanValidateFunction(modelValue, { country } = {}) {
|
|
||||||
...
|
|
||||||
return {
|
|
||||||
'my-app-isIban' : regexes(country || 'us').test(modelValue.trim());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The resulting `lion-validate+my-app-isIban` namespace is now guarantueed to be unique.
|
In order for the localization data to be found, the translation files need to be added to the manager of [localize](../../../localize/).
|
||||||
|
|
||||||
In order for the localization data to be found, the translation files need to be added to the
|
|
||||||
manager of [localize](../../../localize/).
|
|
||||||
The recommended way to do this (inside your `validators.js` file):
|
The recommended way to do this (inside your `validators.js` file):
|
||||||
|
|
||||||
```js
|
```js
|
||||||
localize.loadNamespace({
|
localize.loadNamespace({
|
||||||
'lion-validate+my-app-isIban': (locale) => {
|
'lion-validate+my-app-isIban': locale => {
|
||||||
return import(`./translations/${locale}.js`);
|
return import(`./translations/${locale}.js`);
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
In (for instance) `./translations/en-GB.js`, we will see:
|
In (for instance) `./translations/en.js`, we will see:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
export default {
|
export default {
|
||||||
error: {
|
error: {
|
||||||
'my-app-isIban': 'Please enter a(n) valid IBAN number for {fieldName}.',
|
'my-app-isIban':
|
||||||
|
'Please enter a(n) valid {validatorParams.country} IBAN number for {fieldName}.',
|
||||||
},
|
},
|
||||||
warning: {
|
warning: {
|
||||||
'my-app-isIban': 'Please enter a(n) valid IBAN number for {fieldName}.',
|
'my-app-isIban':
|
||||||
|
'Please enter a(n) valid {validatorParams.country} IBAN number for {fieldName}.',
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
<!-- TODO: use error messages for warning validators as backup, so they can be omitted for almost all use cases -->
|
<!-- TODO: use error messages for warning validators as backup, so they can be omitted for almost all use cases -->
|
||||||
|
|
||||||
## Creating a factory function
|
`validatorParams` is the second argument passed to the validator.
|
||||||
|
In this case this is the object `{ country: '%value%' }` where `%value%` is the one passed by an app developer.
|
||||||
Now, with help of the __validate function__, we will create the __Validator__: a factory function.
|
|
||||||
|
|
||||||
```js
|
|
||||||
export const IbanValidator = (...factoryParams) => [
|
|
||||||
(...params) => ({ country: ibanValidateFunction(...params) }),
|
|
||||||
...factoryParams,
|
|
||||||
];
|
|
||||||
```
|
|
||||||
|
|
||||||
## Conclusion
|
## Conclusion
|
||||||
|
|
||||||
|
|
@ -141,5 +145,5 @@ We are now good to go to reuse our validator in external contexts.
|
||||||
After importing it, using the validator would be as easy as this:
|
After importing it, using the validator would be as easy as this:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<validatable-el .errorValidators="${[IbanValidator({ country: 'nl' })]}"></validatable-el>
|
<validatable-el .errorValidators="${[isIbanValidator({ country: 'nl' })]}"></validatable-el>
|
||||||
```
|
```
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue