Angular 4: reactive form control is stuck in pending state with a custom async validator Angular 4: reactive form control is stuck in pending state with a custom async validator angular angular

Angular 4: reactive form control is stuck in pending state with a custom async validator


There's a gotcha!

That is, your observable never completes...

This is happening because the observable never completes, so Angular does not know when to change the form status. So remember your observable must to complete.

You can accomplish this in many ways, for example, you can call the first() method, or if you are creating your own observable, you can call the complete method on the observer.

So you can use first()

UPDATE TO RXJS 6:

briteVerifyValidator(service: Service) {  return (control: AbstractControl) => {    if (!control.valueChanges) {      return of(null);    } else {      return control.valueChanges.pipe(        debounceTime(1000),        distinctUntilChanged(),        switchMap(value => service.getData(value)),        map(data => {          return data.status === 'invalid' ? { invalid: true } : null;        })      ).pipe(first())    }  }}

A slightly modified validator, i.e always returns error: STACKBLITZ


OLD:

.map(data => {   return data.status === 'invalid' ? { invalid: true } : null;}).first();

A slightly modified validator, i.e always returns error: STACKBLITZ


So what I did was to throw a 404 when the username was not taken and use the subscribe error path to resolve for null, and when I did get a response I resolved with an error. Another way would be to return a data property either filled width the username or emptythrough the response object and use that insead of the 404

Ex.

In this example I bind (this) to be able to use my service inside the validator function

An extract of my component class ngOnInit()

//signup.component.tsconstructor( private authService: AuthServic //this will be included with bind(this)) {ngOnInit() { this.user = new FormGroup(   {    email: new FormControl("", Validators.required),    username: new FormControl(      "",      Validators.required,      CustomUserValidators.usernameUniqueValidator.bind(this) //the whole class    ),    password: new FormControl("", Validators.required),   },   { updateOn: "blur" });}

An extract from my validator class

//user.validator.ts...static async usernameUniqueValidator(   control: FormControl): Promise<ValidationErrors | null> { let controlBind = this as any; let authService = controlBind.authService as AuthService;   //I just added types to be able to get my functions as I type  return new Promise(resolve => {  if (control.value == "") {    resolve(null);  } else {    authService.checkUsername(control.value).subscribe(      () => {        resolve({          usernameExists: {            valid: false          }        });      },      () => {        resolve(null);      }    );  }});...


I've been doing it slightly differently and faced the same issue.

Here is my code and the fix in case if someone would need it:

  forbiddenNames(control: FormControl): Promise<any> | Observable<any> {    const promise = new Promise<any>((resolve, reject) => {      setTimeout(() => {        if (control.value.toUpperCase() === 'TEST') {          resolve({'nameIsForbidden': true});        } else {          return null;//HERE YOU SHOULD RETURN resolve(null) instead of just null        }      }, 1);    });    return promise;  }