Valkyrie_30
Valkyrie_30

Reputation: 37

Async validator for unique userId not working

Inside service-

getUserIdToCheckDuplicate(userId:any):Observable<any>{
    const url = ``; //url goes here
    return this.http.get<any>(url);
  }

Inside component-

ngOnInit(): void {
    this.form = this.fb.group({
      userId: ['', [Validators.required],[this.existingUserIdValidator()]]
    }

get userId() {
    return this.form.get("userId");
  }

existingUserIdValidator(initialID: string = ""): AsyncValidatorFn {
    return (
      control: AbstractControl
    ):
      | Promise<{ [key: string]: any } | null>
      | Observable<{ [key: string]: any } | null> => {
      if (control.value === initialID) {
        return of(null);
      } 
      else {
        return control.valueChanges.pipe(
          debounceTime(500),
          take(1),
          switchMap(_ =>
            this.userService
              .getUserIdToCheckDuplicate(control.value)
              .pipe(
                map(user =>
                  user ? { existingUserId: { value: control.value } } : null
                )
              )
          )
        );
      }
    };
  }

Inside html-

 <mat-form-field appearance="outline" fxFlex="1 1 calc(25% - 10px)" fxFlex.lt-md="1 1 calc(25% - 10px)"fxFlex.lt-sm="100%" fxFlex.xs="100%" class="from-color">
    <mat-label class="label-padding">User ID</mat-label>
       <input class="label-padding" formControlName="userId" matInput placeholder="User ID" required />
          <div style="color: red; font-weight: bold;" *ngIf="userId.errors.existingUserId">Already Exists !</div>
 </mat-form-field>

When I start entering anything the input field, it shows "Cannot read property 'existingUserId' of null".

I've already followed various different ways to perform this async validation. But in my case, those never worked! I don't know where I'm making the mistake! Can anyone help me with this?

Upvotes: 0

Views: 253

Answers (1)

G&#233;r&#244;me Grignon
G&#233;r&#244;me Grignon

Reputation: 4228

The type definition of userId.errors is ValidationErrors | null. So if there is no error, you can't access the existingUserId property.

You can fix this way : userId.errors?.existingUserId. It'll only try to access existingUserId if errors is not null.

About the validator itself, you can't get the validation on the expected value by using valueChanges. Here is a solution :

import { timer } from 'rxjs';

existingUserIdValidator(initialID: string = ""): AsyncValidatorFn {
    return (
      control: AbstractControl
    ):
      | Promise<{ [key: string]: any } | null>
      | Observable<{ [key: string]: any } | null> => {
      if (control.value === initialID) {
        return of(null);
      } 
      else {
        return timer(500).pipe(
          switchMap(_ =>
            this.userService
              .getUserIdToCheckDuplicate(control.value)
          ),
          map(user => user ? { existingUserId: { value: control.value } } : null
          )
        );
      }
    };
  }

Here is a naive sample : https://stackblitz.com/edit/angular-ivy-uv3bv6?file=src%2Fapp%2Fapp.component.html

Upvotes: 1

Related Questions