javascriptangulartypescriptangular-formsangular-validator

Can't set angular async validator


I created the following angular async validator class, following the documentation:

import { Injectable } from '@angular/core';
import {
  AsyncValidator,
  AbstractControl,
  ValidationErrors,
} from '@angular/forms';
import { XService } from '../../pages/x/x.service';
import { Observable, of } from 'rxjs';
import { map, catchError } from 'rxjs/operators';

@Injectable()
export class UniqueXValidator
  implements AsyncValidator {
  constructor(
    private xService: XService,
  ) {}

  validate(ctrl: AbstractControl): Observable<ValidationErrors | null> {
    return this.xService.checkExists(ctrl.value).pipe(
      map(exists =>
        exists ? { uniqueX: true } : null,
      ),
      catchError(() => of(null)),
    );
  }
}

Then I try to programmatically attach it to a form control this way:

    this.form.controls['code'].setAsyncValidators(
      UniqueXValidator,
    );

I am getting the following error message displayed as a tooltip on vs code when hovering "UniqueXValidator" in the second piece of code:

    Argument of type 'typeof UniqueXValidator' is not assignable to parameter of type 'AsyncValidatorFn | AsyncValidatorFn[]'.
  Type 'typeof UniqueXValidator' is missing the following properties from type 'AsyncValidatorFn[]': pop, push, concat, join, and 25 more.ts(2345)

EDIT: It was maybe a problem of compatibility between my angular version (7) and the documentation's (11)


Solution

  • Use AsyncValidatorFn instead AsyncValidator like this:

    usernameValidator(): AsyncValidatorFn {
      return (control: AbstractControl): Observable<ValidationErrors | null> => {
        return this.checkIfUsernameExists(control.value).pipe(
          map(res => {
            // if res is true, username exists, return true
            return res ? { usernameExists: true } : null;
            // NB: Return null if there is no error
          })
        );
      };
    }
    

    then you can add a validator at initialization:

    this.fb.group({
      username: [
        null, [Validators.required],  [this.usernameService.usernameValidator()]
      ]
    });
    

    or at runtime:

    this.form.controls['code'].setAsyncValidators(
      this.usernameService.usernameValidator()
    );