Skip to content Skip to sidebar Skip to footer

Angular 4: Reactive Form Control Is Stuck In Pending State With A Custom Async Validator

I am building an Angular 4 app that requires the BriteVerify email validation on form fields in several components. I am trying to implement this validation as a custom async valid

Solution 1:

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


Solution 2:

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 empty through 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.ts

constructor(
 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);
      }
    );
  }
});

...

Solution 3:

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;
  }

Post a Comment for "Angular 4: Reactive Form Control Is Stuck In Pending State With A Custom Async Validator"