Christian Phillips
Christian Phillips

Reputation: 18749

Angular 11 AsyncValidator not returning error

I have a basic form that checks if an email address exists as an Async Validator.

ngOnInit(): void {
      this.form = new FormGroup({
        email: new FormControl('',
        [
          Validators.required,
          Validators.pattern('^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,4}$')
         ],
         [
           pendingPortfolioUserRequestValidator(this.portfolioInviteService, this.data.portfolioId)
          ])
      });
  }

The Async Validator pendingPortfolioUserRequestValidator looks like this

export function pendingPortfolioUserRequestValidator(portfolioInviteService: PortfolioInviteService,
  portfolioId: number): AsyncValidatorFn {
  return (control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
    return portfolioInviteService.checkUserIsPending(portfolioId, control.value).pipe(map((email) => {
      return (email) ? {'pendingUserExists': true} : null;
    }));
  };
}

The line of code that determines whether the email exists or not (return (email) ? {'pendingUserExists': true} : null;) is getting hit, but when I check the errors collection, it's giving null as the value.

I have also tried to use a promise, but nothing seems to work.

Upvotes: 1

Views: 871

Answers (1)

Christian Phillips
Christian Phillips

Reputation: 18749

On further reading, and trying to get to the bottom of this, I found a pending property. When I checked the state of this, the property was still pending even though it looked like the validation was complete. This part of the article helped with the understanding:

Asynchronous validators implement the AsyncValidatorFn and AsyncValidator interfaces. These are very similar to their synchronous counterparts, with the following differences.

  • The validate() functions must return a Promise or an observable,
  • The observable returned must be finite, meaning it must complete at some point. To convert an infinite observable into a finite one, pipe the observable through a filtering operator such as first, last, take, or takeUntil.

I needed to add a call to take(1), and this has solved the issue.

Updated Code:

export function pendingPortfolioUserRequestValidator(portfolioInviteService: PortfolioInviteService,
  portfolioId: number): AsyncValidatorFn {
  return (control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
    return portfolioInviteService.checkUserIsPending(portfolioId, control.value).pipe(
      take(1),
      map(
      (email: boolean) => {
      return (email) ? {'pendingUserExists': true} : null;
    }));
  };
}

Upvotes: 2

Related Questions