11

I have a Custom Validator that validates if a user's email is unique or not.

I've searched for related topic on stackoverflow and internet, but no help

when I'm giving input in the form (sending the request), the request stays at pending state and doesnot resolves.

I've tested the backend in postman, its working as expected, the problem is at client or angular side.

The Validation function is as follows:

emailUniqueValidator (control: AbstractControl): Promise<{[key: string]: any}> | Observable<{[key: string]: any}> {
    return new Promise((resolve, reject) => {
      this.userService.checkEmailUnique(control.value).subscribe((response: Response) => {
        const body = response.json();
        if (body.found === false) {
          resolve(null); // email is unique
        } else {
          reject({emailUniqueValidator: true}); // email is not unique
        }
      });
    });
  }

When i'm changing the form control's value, the pending class appears, but it sticks in forever instead of getting resolved.

I have the email form control as follows:

'email': new FormControl('', [
        Validators.required,
        Validators.email
      ], this.emailUniqueValidator.bind(this)
),

And my User service is as follows:

checkEmailUnique (email: string): Observable<Response> {
    return this.http.post('http://localhost:3000/user/email', {email}, { withCredentials: true });
  }

Why The validator is not resolving in time and staying pending forever?

EDIT:

The following code is getting value from the service just fine, i've checked it by logging the response into the console.

If the email is not present in the database, then it is getting resolved just fine, but if the email is already present, it is throwing an error and the state remains pending.

return new Promise((resolve, reject) => {
      this.userService.checkEmailUnique(control.value).subscribe((response: Response) => {
        console.log(response);
        response.json().found ? reject({emailUniqueValidator: true}) : resolve(null);
      }, (error: any) => reject({emailUniqueValidator: true}));
});
Jnr
  • 1,504
  • 2
  • 21
  • 36
Himanshu Mittal
  • 584
  • 1
  • 6
  • 21
  • See https://github.com/angular/angular/issues/13200 – Hypenate Aug 06 '19 at 06:16
  • has your component ChangeDetectionStrategy.OnPush ? – Eldar Nov 12 '19 at 11:42
  • `emailUniqueValidator (control: AbstractControl): Promise<{[key: string]: any}> | Observable<{[key: string]: any}> { return this.userService.checkEmailUnique(control.value).pipe( debounceTime(500), distinctUntilChanged(), map(value => value.body.found ? {emailUniqueValidator: true} : null) ); }` This code is working fine for me @Eldar – Himanshu Mittal Nov 22 '19 at 17:27

4 Answers4

21

The observable never completes.

Try adding .first() at the end.

emailUniqueValidator (control: AbstractControl): Observable<{[key: string]: any}> {
    return this.userService.checkEmailUnique(control.value)
               .map(r => r.json())
               .map(r => (r.found) ? {emailUniqueValidator: true} : null)
               .first();
 }
Joey V.
  • 1,866
  • 1
  • 22
  • 18
  • 1
    The more recent rxjs version looks like this: `this.userService.checkEmailUnique(control.value).pipe(map(r => r.json()), take(1))` Some details left out to keep it readable, as code formatting options are limited in comments apparently. – Paul Nov 29 '21 at 11:16
3

I had the same problem. I removed changeDetection: ChangeDetectionStrategy.OnPush and it worked. An example is here.

wonderful world
  • 10,969
  • 20
  • 97
  • 194
2

make sure your "status code from" service is 200 any case

var user = _appRepository.GetUserByUserName(username);
if (user != null)

    return Ok(user );
}
return Ok();

this is from asp.net core api. when user not available send empty Ok(). if angular catches 404 or 400, pending always show.

export function uniqueUsernameValidator(userService: UserService): AsyncValidatorFn {
  return (c: AbstractControl): Observable<{ [key: string]: any } | null> => {
    return userService.getUserByUsername(c.value).pipe(
      map(users => {
        console.log(users)
        if (users) {
          return { 'unique': true};
        }else {
          return null
        }
      })
    )
  };
}
FortyTwo
  • 2,414
  • 3
  • 22
  • 33
1

Why are you going through the promise wrapping an observable head ache? try it like this:

emailUniqueValidator (control: AbstractControl): Observable<{[key: string]: any}> {
    return this.userService.checkEmailUnique(control.value)
               .map(r => r.json())
               .map(r => (r.found) ? {emailUniqueValidator: true} : null);
 }
bryan60
  • 28,215
  • 4
  • 48
  • 65