Parametrized Validators in Dynamic Forms
Tags: angular, dynamic, forms, validators
Published: 2021-05-03 15:16:04

---
series: Dynamic Forms for Angular
---
> **TL;DR**
> We are building the documentation of `@myndpm/dyn-forms` at [mynd.dev](https://mynd.dev/docs/dyn-forms/) and we've added support for a variety of custom functions like Validators, AsyncValidators, Matchers, Conditions and more.

The next crucial part of any form is validation, aka `Validators` and `AsyncValidators`, and we took some time to study a nice way to implement them and we picked the most declarative one:

```typescript
createMatConfig('INPUT', {
  name: 'quantity',
  validators: ['required', ['min', 1] ],
  asyncValidators: ['myAsyncValidator'],
```

## Angular Validators

Angular provides default [Validators](https://angular.io/api/forms/Validators) that we're used to consume programatically in our Reactive Forms, some of them are Validator Functions (`ValidatorFn`) like `Validators.required`, and some others are Validator Factories (`(args) => ValidatorFn`) which builds a Validator based on a required parameter like `Validators.minLength(4)`.

The definition of a Validator Function is:
```typescript
(control: AbstractControl) => ValidationErrors | null
```
it receives the control to be validated, and returns `null` if its value is valid, or an error object of the form `{ [error: string]: any }`

Validator Factories are high-order functions that builds a Validator Function according some input parameters:
```typescript
function minLength(minLength: number): ValidatorFn {
  return (control: AbstractControl) => {
    return (control.value && control.value.length < minLength)
      ? { minLength: true } // invalid
      : null; // valid
  }
}
```
as you can see, this is a very nice way to parametrize our Functions, so we defined the provisioning of Validators (and all the other handlers) with an `id` and a factory `fn`:

```typescript
export interface DynControlValidator {
  id: string;
  fn: (...args: any[]) => ValidatorFn;
}
```

The `id` will be the string that we will use in our Configuration Object. By default, `@myndpm/dyn-forms` provide the default Angular Validators with the same name as we know them: `required`, `requiredTrue`, `email`, `pattern`, `minLength`, `maxLength`, `min` and `max`.

The notation to use them in the Config Object is as follows:

```typescript
// without parameters
validators: ['required'],

// with parameters as array
validators: ['required', ['min', 1] ],

// with parameters as object
validators: { required: null, minLength: 4 },

// with an inline ValidatorFn or ValidatorFn factory
validators: [myValidatorFn, myValidatorFactory(args)],

```
supporting these different notations is unexpensive and can be useful for different kind of systems or developer tastes.

## Custom Validators

You can provide inline functions to build a fast prototype, but to store a plain config somewhere, you need to provide your `ValidatorFn` Factory with an `id` and a `fn` in the respective module with a code like this:

```typescript
import { AbstractControl, ValidatorFn } from '@angular/forms';
import { DynFormsModule } from '@myndpm/dyn-forms';
import { DynControlValidator } from '@myndpm/dyn-forms/core';

const validators: DynControlValidator[] = [
  {
    id: 'email',
    fn: (): ValidatorFn => {
      return (control: AbstractControl) => {
        // implement my validator
        // to return { email: true } | null;
      }
    }
  }
];

@NgModule({
  imports: [
    DynFormsModule.forFeature({ validators, priority: 100 });
```
note the `priority` parameter to override the default validators (which weight is 0); we will play with priorities in a further article.

## AsyncValidators

Providing async validators works in the same way. You provide your `fn` with an `id` and use them in the Config Object:

```typescript
createMatConfig('INPUT', {
  name: 'quantity',
  validators: ['required'],
  asyncValidators: ['myAsyncValidatorId'],
```
and if you need to provide arguments to your AsyncValidator factory, you can use:
```typescript
// single argument which can be an object
asyncValidators: [['myAsyncValidatorId', args]],
// your factory will receive fn(args)

// multiple arguments in array to be destructured
asyncValidators: [['myAsyncValidatorId', [arg1, arg2]]],
// your factory will receive fn(arg1, arg2)
```

## Custom Handlers

With this notation we added support for multiple kinds of functions that we require in the Dynamic Forms: `Validators` and `AsyncValidators` as we just saw, `Matchers` and `Conditions` to manipulate the controls under some special requirements, and also `ParamFns` to inject functions to the parameters of the DynControls too.

We will be digging into the conditional executions in the next chapter.
In the meantime, what do you think of this notation?

*// PS. We are hiring!*