简体   繁体   中英

validator to return Promise or Observable : Custom async validator : Angular 8

I'm trying to create a custom async validator for my registration form where it checks if an email is a valid email or not using third party API. this is ref link of the API website -

https://www.zerobounce.net/email-validation-api.html

I am trying to implement it using RxJs debounceTime, distinctUntilChanged. In the form control I have two more validations required and pattern. But I am always getting this error - Error: Expected validator to return Promise or Observable.

I have searched several examples but nothing worked. Thank you in advance.

Validator -

export class UniqueEmailValidator{
   static createValidator(_ajaxService : AjaxService){
        return (control : AbstractControl) =>{
            const apiKey = environment.emailValidatationKey;
            const baseUrl = 'https://api.zerobounce.net/v2/validate?';
            if(control.valid){
                return control
                .valueChanges
                .pipe(
                    debounceTime(800),
                    distinctUntilChanged(),
                    switchMap((email : string) => _ajaxService.apiCall('', `${baseUrl}api_key=${apiKey}&email=${email}&ip_address=''`, 'GET', true)),
                    map(res => res.json()),
                    map((validationStatus : any) => {
                        if (
                            validationStatus.status == "valid" &&
                            validationStatus.mx_found == "true"
                        ) {
                            return null
                        } else {
                            return { isEmailInvalid : true }
                        }
                    })
                )
            }
        }
    }
}

Register Component -

this.registration = this._formBuilder.group({
  firstName: new FormControl('', [
    Validators.required,
    Validators.pattern('^[a-z A-Z]+$')
  ]),
  lastName: new FormControl('', [
    Validators.required,
    Validators.pattern('^[a-z A-Z]+$')
  ]),
  email: new FormControl('', [
    Validators.required,
    Validators.pattern('[A-Za-z0-9._%-]+@[A-Za-z0-9._%-]+\\.[a-z]{2,3}')
  ],UniqueEmailValidator.createValidator(this._ajaxService))
})

Why are you piping validators Observable from valueChanges Observable? That doesn't make much sense since the validator runs on valueChange and when it's dirty. And you are also returning the desired Observable only if the control is valid . So when sync validators mark the control as Invalid this one won't return the Observable. I guess that's what's causing the error.

Try this approach:

export class UniqueEmailValidator {
  static createValidator(_ajaxService: AjaxService) {
    return (control: AbstractControl) => {
      const apiKey = environment.emailValidatationKey;
      const baseUrl = 'https://api.zerobounce.net/v2/validate?';

      return timer(800).pipe(
        concatMap((email: string) =>
          _ajaxService.apiCall('', `${baseUrl}api_key=${apiKey}&email=${email}&ip_address=''`, 'GET', true)
        ),
        map(res => res.json()),
        map((validationStatus: any) => {
          if (validationStatus.status == 'valid' && validationStatus.mx_found == 'true') {
            return null;
          } else {
            return { isEmailInvalid: true };
          }
        })
      );
    };
  }
}}

I'm using similar approach to this one in a project where I have to check export path on the filesystem. One thing I'm not so sure about is distinctUntilChanged. Isn't that filtered already

This what I have ended up with, which resolves my problem -

Validator -

export class UniqueEmailValidator {
    static createValidator(_ajaxService: AjaxService) {
        let validatorSubject$ = new Subject();
        let debouncedSubject$ = new Subject<string>();

        debouncedSubject$
        .pipe(debounceTime(500), distinctUntilChanged())
        .subscribe((email: string) => {
            const apiKey = environment.emailValidatationKey;
            const baseUrl = 'https://api.zerobounce.net/v2/validate?';
            _ajaxService.apiCall('', `${baseUrl}api_key=${apiKey}&email=${email}&ip_address=''`, 'GET', true).subscribe({
                next: (validationData : IEmailValidation) => {
                    if (
                        validationData.status == "valid" &&
                        validationData.mx_found == "true" &&
                        (
                          validationData.sub_status == "alias_address" ||
                          validationData.sub_status == ""
                        )
                    ) {
                        return null
                    } else {
                        return { isEmailInvalid : true }
                    }
                },
                error: (validationFailed) => {
                    console.log(
                        "Failed to validate the Email Address",
                        validationFailed
                    );
                },
            });
        });

        return (
            control: AbstractControl
        ):
        | Promise<ValidationErrors | null>
        | Observable<ValidationErrors | null> => {
            debouncedSubject$.next(control.value);
            let promise = new Promise<any>((resolve, reject) => {
              validatorSubject$.subscribe((result) => resolve(result));
            });
            return promise;
        };
    }
}

Component TS -

this.registration = this._formBuilder.group({
  firstName: new FormControl('', [
    Validators.required,
    Validators.pattern('^[a-z A-Z]+$')
  ]),
  lastName: new FormControl('', [
    Validators.required,
    Validators.pattern('^[a-z A-Z]+$')
  ]),
  email: new FormControl('', [
    Validators.required,
    Validators.pattern('[A-Za-z0-9._%-]+@[A-Za-z0-9._%-]+\\.[a-z]{2,3}')
  ],UniqueEmailValidator.createValidator(this._ajaxService))
}) 

Component HTML -

<div *ngIf="formcontrol.email.errors.isEmailInvalid">
   <p>Use a working E-mail address.</p>
</div>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM