1
votes

I'm doing a web application in Angular 10 with a simple form to receive two values that I will be validating them on the backend, doing a HTTP call. To accomplish this I have created an async validator which runs perfectly.

Problem: It's not setting the error to the FormGroup. In other words, the FormGroup is always valid.

this.form = this.fb.group({
  // I will validate this two values with the backend
  patientIdentifications: this.fb.group({
    clinicRecord: [null, Validators.required],
    documentId: [null, Validators.required]
  }, {
    updateOn: 'blur',
    asyncValidators: CustomValidators.isPatientValid(this.myService) // <= async validator
  }),
  // Just to illustrate that I have more FormControls
  firstName: [null, Validators.required],
});

Async validator

export class CustomValidators {

  static isPatientValid(myService: MyService): AsyncValidatorFn {
    return (formGroup: FormGroup):
      Promise<ValidationErrors | null> |
      Observable<ValidationErrors | null> => {

      const clinicRecordControl = formGroup.controls.clinicRecord;
      const documentIdControl = formGroup.controls.documentId;
      const clinicRecordValue = clinicRecordControl.value;
      const documentIdValue = documentIdControl.value;

        return myService.getPatient(clinicRecordValue, documentIdValue).pipe(
          map(patient => patient ? of(null) : of({valid: true})),
          catchError(() => of(null))
        );
    };
  }

}

The HTTP call is done perfectly when the two inputs loses the focus. But the error is not being set in the FormGroup.

I have tried these solutions:

#1. Adding bind() to the validator call

patientIdentifications: this.fb.group({
        clinicRecord: [null, Validators.required],
        documentId: [null, Validators.required]
      }, {
        updateOn: 'blur',
        asyncValidators: CustomValidators.isPatientValid(this.myService).bind(this) // <= bind
      }),

#2. Remove the of function

return myService.getPatient(clinicRecordValue, documentIdValue).pipe(
              map(patient => patient ? null : {valid: true}), // <= remove the "of"
              catchError(() => of(null))
            );

#3. Setting the error directly using the FormGroup's instance

return myService.getPatient(clinicRecordValue, documentIdValue).pipe(
              map(patient => patient ? formGroup.setErrors(null) : formGroup.setErrors({valid: true})),
              catchError(() => of(null))
            );

None of the solutions have worked for me.

My goal is to set the error to the FormGroup correctly to have the FormGroup as INVALID, which is the correct thing to do.

1
Shouldn't catchError(() => of(null)) return of({valid: true}) since it's an error?Chrillewoodz
Also try swapping map for switchMap.Chrillewoodz
@Chrillewoodz do you mean doing return formGroup.valueChanges.pipe(switchMap... or returning the switchMap directly?RRGT19
return myService.getPatient(clinicRecordValue, documentIdValue).pipe( switchMap(patient => patient ? of(null) : of({valid: true})), catchError(() => of(null)) );Chrillewoodz
@Chrillewoodz Believe it or not, your first comment solved my issue, I need to return of({valid: true}) inside of the catchError. Also, it works with switchMap or map, although I don't understand the difference. I know what switchMap is for, but never seen it used like this. Lastly, I'm not sure if the correct thing to do is of(null) or formGroup.setErrors(null). If you could place the answer with the best approach I will accept it.RRGT19

1 Answers

1
votes

Right now you're saying that errors are OK since you're not returning of({valid: true}) but rather of(null). If you change it to this instead it will work:

return myService.getPatient(clinicRecordValue, documentIdValue).pipe(
      map(patient => patient ? of(null) : of({valid: true})),
      catchError(() => of({valid: true))
    );
};

Based on your comment which asks whether to return an observable or use formGroup.setErrors(null) I would go with an observable as it seems to be the recommended way when looking at the docs for custom validators. This is irregardless of when you're using async or synchronous validators.