Custom Validator Not Working in Angular Reactive Form

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!


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