How to create a custom Angular async validator
Tags: angular
Published: 2021-02-04 12:03:43

Angular provides a certain number of ways to validate your input data. With attributes like `required` or `maxlength` we can easily control what a user can and cannot insert in a form.

However, what if the validation has to happen on the backend? How do I implement a custom validator that is able to call a backend service and return the validation result?

In this article, I will guide you through the steps you need to perform in order to create your custom asynchronous validator.

## The backend service
For the purpose of this example, let's imagine we have an endpoint which accepts, in the request body, a structure like the following one:
```
{
   "objectType": "SOME_OBJECT_TYPE",
   "value": "Some name"
}
```
Where `value` is the value to validate and `objectType` contains some information needed by the sever.

To this call, the server responds with another object containing the field `valid` (the validation result) and a message:
```
{
   "valid": false,
   "message": "Must not be blank"
}
```

## The validator
To create a custom validator, we need to create a new directive by running:
```
ng generate directive nameIsValid
```
Let's define the directive decorator first:
```
@Directive({
  selector: '[nameIsValid][ngModel],[nameIsValid][FormControl]',
    providers: [
        {
            provide: NG_ASYNC_VALIDATORS,
            useExisting: NameIsValidDirective, 
            multi: true
        }
    ]
})
```
The code above registers the validator, so Angular is aware it exists and can use it during binding. This means that thereafter we can use it in an input field as in the following example:
```
<input
  type="text"
  id="name"
  name="name"
  nameIsValid
>
```
Moving back to the directive, we can now populate the validator class:
```
export class NameIsValidDirective implements AsyncValidator {

  private validationServiceUrl = "my backend url";

  constructor(
    private http: HttpClient
  ) { }

}
```
As you can see, the `NameIsValidDirective` class implements the `AsyncValidator` interface which requires us to define a `validate` function:
```
validate(control: AbstractControl): 
   Observable<ValidationErrors | null> {

    let fieldToValidate = {
        objectType: "NAME_VALUE",
        value: control.value
    };

    const request = this.validationServiceUrl

    const obs = this.http.post<any>(request, fieldToValidate)
        .pipe(
            map((validationResult) => {
                // null no error, object for error
                return validationResult.valid === true ? null : {
                  NameIsValidDirective : validationResult.message
                };
            })
        );
    return obs;
  }
```
`control.value` contains the value the user inserted in the input field. With this information, we build the object to send to the backend, make a call and get the response: if the validation went good, we must return `null`, otherwise we must return an object. In this example, I chose to return the validation error message.

## Conclusions
Despite typically there is no need to create custom validators at all, there are specific cases or architectural needs where we require to perform a validation on the backend side. When this case occurs, you are now aware of how custom validators work and you successfully learned how to implement one.