Angular 6 - Expected validator to return Promise or Observable in async validator

16,534

Solution 1

RxJS's map don't take a second function argument.
I guess you want something like this:

if (new RegExp(validationUtils.emailRegex).test(email)) {
  return this.userService
  .checkIfEmailExist(email)
  .pipe(
    map(data => {emailExist: true}),
    catchError(error => null)
  );
}

return of(null);

Solution 2

When you add multiple validators, then you need to add your validators inside another third bracket '[]' . Like Below:

this.yourForm= this.formBuilder.group({
    amount: [null, [Validators.required, Validators.min(1)]],
});

Solution 3

You have to return the observable from checkIfEmailExist:

// ...
return this.userService
  .checkIfEmailExist(email)
  .pipe(
// ...
Share:
16,534

Related videos on Youtube

Michał Bil
Author by

Michał Bil

Updated on September 16, 2022

Comments

  • Michał Bil
    Michał Bil over 1 year

    In my Angular 6 app I have async validator for checking if typed email is already registered but right now I'm getting 'Expected validator to return Promise or Observable' and I don't really know why.

    import { UserService } from '../user/service/user.service';
    import { AbstractControl, ValidationErrors, AsyncValidator } from '@angular/forms';
    import * as validationUtils from '../validation/validation-utils';
    import { Injectable } from '@angular/core';
    import { of, Observable } from 'rxjs';
    import { map } from 'rxjs/operators';
    
    @Injectable({
      providedIn: 'root'
    })
    export class EmailExistValidator implements AsyncValidator {
      constructor(private userService: UserService) {
      }
    
      validate(emailControl: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
        const email = emailControl.value;
    
        if (new RegExp(validationUtils.emailRegex).test(email)) {
          this.userService
          .checkIfEmailExist(email)
          .pipe(
            map(
            (data) => {
              return {emailExist: true};
            },
            (error) => {
              return null;
            }
          ));
        }
          return of(null);
      }
    }
    

    register-component.ts

      createForm(): FormGroup {
        return this.formBuilder.group({
          email: ['',
          [Validators.required, Validators.email],
          [EmailExistValidator.bind(this)]],
          username: ['', [Validators.required,
            Validators.pattern(validationUtils.usernameRegex)]],
          password: ['', [Validators.required,
            Validators.pattern(validationUtils.passwordRegex)]],
          repeatPassword: ['', [Validators.required]]
      }, {
        validator: EqualPasswordValidator.validate
      });
      }
    

    Edit 17.08.2018 As @Paulie suggested I changed validator declaration in formbuilder from .bind to pointing to validate function and with some tweaks to his proposition now everything works!

    Final code:

    register-component.ts

      constructor(
        private formBuilder: FormBuilder,
        private userService: UserService,
        private emailExistsValidator: EmailExistsValidator,
        private usernameExistsValidator: UsernameExistsValidator) { }
    
      ngOnInit() {
        this.registerForm = this.createForm();
      }
    
      createForm(): FormGroup {
        return this.formBuilder.group({
          email: ['',
          [Validators.required, Validators.email],
          [this.emailExistsValidator.validate]],
          username: ['', [Validators.required,
            Validators.pattern(validationUtils.usernameRegex)],
            [this.usernameExistsValidator.validate]],
          password: ['', [Validators.required,
            Validators.pattern(validationUtils.passwordRegex)]],
          repeatPassword: ['', [Validators.required]]
      }, {
        validator: EqualPasswordValidator.validate
      });
      }
    

    email-exists-validator.ts

    import { UserService } from '../user/service/user.service';
    import { AbstractControl, ValidationErrors, AsyncValidator } from '@angular/forms';
    import * as validationUtils from './validation-utils';
    import { Injectable } from '@angular/core';
    import { of, Observable } from 'rxjs';
    import { map, catchError } from 'rxjs/operators';
    
    @Injectable({
      providedIn: 'root'
    })
    export class EmailExistsValidator implements AsyncValidator {
        static userService: UserService;
    
      constructor(private userService: UserService) {
        EmailExistsValidator.userService = userService;
      }
    
      validate(emailControl: AbstractControl): Promise<{ [key: string]: any } | null>
      | Observable<{ [key: string]: any } | null>  {
    
        const email = emailControl.value;
    
        if (new RegExp(validationUtils.emailRegex).test(email)) {
            return EmailExistsValidator.userService
            .checkIfEmailExist(email)
            .pipe(
              map(data => ({emailExist: true})),
              catchError(error => of(null))
            );
          }
    
          return of(null);
    
    }
    }
    
  • Paulie
    Paulie over 5 years
    Are there any console errors? .bind(this) may mess up with the userService injector. Try to remove it.
  • Michał Bil
    Michał Bil over 5 years
    Yeah you was right. Bind was causing errors in this case. I edited my question with final working version.
  • Dragos Durlut
    Dragos Durlut over 5 years
    +1 for the return of(null). Still have to understand exactly what it does, but it fixed the error for me. I was returning just null.
  • Paulie
    Paulie over 5 years
    of(null); creates the RxJS Subject (object you can .subscribe()) which always 'sends' null (or other value/value sequence in you specify in the parameter) to the Subscriber. learnrxjs.io/operators/creation/of.html