1

I am trying to create a custom validator that checks if a phone number already exists. My code is as follows:

Validation Function

phoneValidator() {
    return (control: AbstractControl): Observable<ValidationErrors | null> => {
      if (!control.valueChanges || control.pristine) {
        return of(null);
      } else {
        this.userMgt.phoneExists(control.value).pipe(
          distinctUntilChanged(),
          debounceTime(600))
          .subscribe((res: ApiResponse<any>) => {
            debugger
            if(!res.hasErrors()) {
              if(res.data == true) {
                return { phoneExists: true };
              }
              else {
                return null
              }
            }
        })
      }
    };
  }

The API from the backend that verifies the phone number:

phoneExists(phoneNo: string): Observable<ApiResponse<user>> {
        return this.clubApiGet(`/profile/isPhoneNumberExists?phoneNo=${phoneNo}`)
    }

My Form Group

private fb: FormBuilder,
defaultUser: User = {
    phoneNo: ""
  };

ngOnInit(): void {
    this.initUserForm();
  }

initUserForm() {
    this.userForm = this.fb.group({
      phone: [
        this.defaultUser.phoneNo,
        Validators.compose([
          Validators.required,
          Validators.minLength(11),
          Validators.maxLength(14),
          this.phoneValidator()
        ]),
      ]
    });
  }

My HTML

<div class="form-group row">
  <label class="col-xl-3 col-lg-3 col-form-label">Contact Phone
    <span class="text-danger">*</span>
  </label>
<div class="col-lg-9 col-xl-6">
  <div class="input-group input-group-lg input-group-solid">
     <div class="input-group-prepend">
      <span class="input-group-text">
       <i class="fas fa-phone-alt text-primary"></i>
      </span>
     </div>
     <input
       type="text"
       class="form-control form-control-lg form-control-solid"
       value="+35278953712"
       placeholder="Phone"
       formControlName="phone"
       [ngClass]="{ 'is-in valid': userForm.controls['phone'].invalid&& 
       userForm.controls['phone'].errors}"
      >
</div>
<ng-container [ngTemplateOutlet]="formError" [ngTemplateOutletContext]="{
    validation: 'required',
    message: 'Phone Number is required',
    control: userForm.controls['phone']
}"></ng-container>

<ng-container [ngTemplateOutlet]="formError" [ngTemplateOutletContext]="{
    validation: 'minlength',
    message: 'Phone Number should at least be 11 characters',
    control: userForm.controls['phone']
}"</ng-container>

<ng-container [ngTemplateOutlet]="formError" [ngTemplateOutletContext]="{
    validation: 'maxlength',
    message: 'Phone Number cannot exceed 14 characters',
    control: userForm.controls['phone']
}"</ng-container>

// THIS VALIDATION CHECK DOES NOT WORK
<div class="text-danger" *ngIf="userForm.controls['phone']?.errors?.phoneExists">
   This Phone Number is already registered to a user
</div>
</div>
</div>

I cannot figure out where the problem occurs. The API is working fine but the error message doesn't show. I would really appreciate any help. Thank You!

3
  • The problem is that you need make an asynchronous validator Commented Jan 22, 2022 at 12:08
  • You've placed the this.phoneValidator() inside the synchronous validators. You have to close the array and start a new array. Commented Jan 22, 2022 at 12:09
  • @Eliseo Could you please answer this question? Commented Jan 22, 2022 at 12:34

1 Answer 1

2
  • You have added this.phoneValidator() as part of synchronous validators. It should be part of Async validators:
phone: [
  this.defaultUser.phoneNo,
  Validators.compose([
    Validators.required,
    Validators.minLength(11),
    Validators.maxLength(14)
  ]),
  [this.phoneValidator()]
]
  • You don't need to subscribe, but rather return an Observable from the async validator function:
// else block 
return this.userMgt.phoneExists(control.value).pipe(  // added 'return'
  distinctUntilChanged(),
  debounceTime(600),
  map((res: ApiResponse<any>) => { // using map operator
    debugger
    if (!res.hasErrors()) {
      if (res.data == true) {
        return { phoneExists: true };
      }
      else {
        return null
      }
    }
    return null; // added return statement
  })
);
Sign up to request clarification or add additional context in comments.

2 Comments

I tried this way and now an error occurs in the html. Whenever I type in the input, I get an error message in the console saying 'Cannot read properties of null (reading 'phoneExists')'. What's causing this?
@Mohid In your code somewhere in ts or html file, you would have errors.phoneExists, which might be causing the issue. You should ensure that errors in not null before accessing phoneExists. In the html template shared, you have something like errors?.phoneExists which shouldn't cause any issue as ? takes care of it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.