Angular async Validator for Reactive Forms

When you need to query the database or hit your API you need to wait for the result to return.
This validator expects a boolean as a return value from the back-end.

slow.async-validator.ts

import { Injectable } from '@angular/core';
import { AbstractControl, ValidationErrors } from '@angular/forms';
import { Observable, of } from 'rxjs';
import { delay, map } from 'rxjs/operators';

@Injectable(
    { providedIn: 'root' }
)
/**
 * Returns undefined after a delay of 1 second
 */
export class SlowValidator {

    constructor() { }

    validate(control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
// Inject a service in the constructor and do a call & map it:
        return of(undefined)
            .pipe(
               map((value: any) => {
        return (value) ? { asyncValue: true } : undefined);
      },
        catchError(() => { asyncValue: true })
      )
            );
    }

    registerOnValidatorChange?(fn: () => void): void {
        throw new Error('Method not implemented.');
    }
}

slow.component.ts

import { Component } from '@angular/core';
import { FormGroup, Validators, FormBuilder } from '@angular/forms';
import { SlowValidator } from './validators/Eslow.async-validator';

@Component({
  selector: 'app-async-validators',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class SlowComponent {
  constructor(
    private _fb: FormBuilder,
    private _slowValidator: SlowValidator,
  ) { }

 this.form = this._fb.group({
      field: ['',
        [ // Regular.
          Validators.required,
        ],
        [ // Async.
          this._slowValidator.validate.bind(this._slowValidator)
        ]
      ]
    });
  }
}

component.html

<form novalidate [formGroup]="form">
  <input type="text" formControlName="field" />
  <div *ngIf="form.get('field').errors?.asyncValue">
    The validator did not return 'undefined', so there is an error
  </div>
</form>

Leave a Reply