How to use validator directive with asynchronous call

Viewed 49

I have created a validator directive that makes an HTTP request that checks if an email already exists.

I can see that the API call happening but in the template the validation is not working

Directive:

@Directive({
    selector:
        '[emailValidator][formControlName],[emailValidator][formControl],[emailValidator][ngModel]',
    providers: [
        {
            provide: NG_ASYNC_VALIDATORS,
            useExisting: forwardRef(() => EmailValidatorDirective),
            multi: true
        }
    ]
})
export class EmailValidatorDirective implements Validator {
    constructor(private _http: HttpService) {}

    validate(
        c: AbstractControl
    ): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
        return this.validateEmail(c.value);
    }

    private validateEmail(email: String): Observable<ValidationErrors | null> {
        return this._http.post('user/validate-email', { email }).pipe(
            debounceTime(500),
            distinctUntilChanged(),
            map((isUsed) => {
                // null no error, object for error
                return !isUsed
                    ? null
                    : {
                            isTaken: 'Email exists already.'
                      };
            })
        );
    }
}

Template:

<input
    id="email"
    type="email"
    class="form-control"
    formControlName="email"
    emailValidator
    placeholder="Enter E-mail Address"
/>

// always showing null
{{ formcontrol['email'].errors | json }}
1 Answers

You need implements AsycnValidator not Validator

If you want, you can also include in the same validator a validator.required and a validator.email

private validateEmail(email: string): Observable<ValidationErrors | null> {
    //required
    if (!email) return of({ required: true });

    //email regExpr
    const EMAIL_REGEXP =
      /^(?=.{1,254}$)(?=.{1,64}@)[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;

    if (!EMAIL_REGEXP.test(email)) return of({ email: true });

    return of(email).pipe(
      debounceTime(500),
      distinctUntilChanged(),
      delay(500),
      map((email) => {
        //check if exist
        return emailsYetGet.find((x) => x == email)
          ? {
              isTaken: 'Email exists already.',
            }
          : null;
      })
    );
  }

a stackblitz

NOTE: Remember include your directive in declarations of the modules where is the components in witch you use it and check you API

NOTE2: the REGEXPR I get it form the Angular's github about validators

Related