Angular custom validation using AsyncValidator with Observable

Aug 6, 2017 • 5 minute read

Summary

The example we’ll build

AsyncValidator

Things to mention

Building the reactive form

Angular’s FormBuilder class can be used to construct new FormGroup, FormControl and FormArray objects that can be used to build an Angular reactive form.

FormBuilder.group(
    controlsConfig: {[key: string]: any}, 
    extra: {[key: string]: any}|null): FormGroup

Excerpt from app.component.ts

  public fruitForm: FormGroup;

  public constructor(
    private formBuilder: FormBuilder,
    private fruitService: FruitService
  ) {
    this.fruitForm = formBuilder.group({
      fruit: [
        '',
        [Validators.required ,Validators.minLength(4)],
        [(control: AbstractControl): Observable<ValidationErrors | null> => 
            this.checkFruitIsApproved$(control)]
      ]
    })
  }

Performing validation

Angular will always process the sync validators first. Only when all of the sync validators pass will angular then process any async validators.

Excerpt from app.component.ts

  public checkFruitIsApproved$(control: AbstractControl): Observable<ValidationErrors | null> {
    return this.fruitService.fruitIsApproved$(control.value)
      /// CODE OMITTED //
  }

Excerpt from fruit.service.ts

@Injectable()
export class FruitService {
  public fruitIsApproved$(fruit: string): Observable<boolean> {
    let approvedFruit: Array<string> = ['apple', 'orange', 'banana', 'pear', 'melon'];
    let isApproved: boolean = approvedFruit.includes(fruit);

    return Observable.of(isApproved)
      .delay(1000);
  }
}

Excerpt from app.component.ts

  public checkFruitIsApproved$(control: AbstractControl): Observable<ValidationErrors | null> {
    return this.fruitService.fruitIsApproved$(control.value)
      .map(response => {
        if (response) {
          return null;
        } else {
          return {checkFruitIsApproved: 'The fruit is not an approved fruit!'};
        }
      });
  }

Our html

<form [formGroup]="fruitForm" novalidate>
  <input [formControlName]="'fruit'">
  <span *ngIf="fruitForm.controls['fruit'].hasError('checkFruitIsApproved')" style="color: red;">
    
  </span>
  <span *ngIf="fruitForm.valid" style="color: green;">
    Outstanding, that fruit is approved!
  </span>
</form>

Wrap up

Contact me

Email me at chum@chumtoadafuq.email.