angularcustom-validators

Angular 5: Reactive Forms - Form group validation retrigger when form control updated


I am using Angular 5 Reactive forms, and have setup a form group say confirmEmail with two form controls as email and confirmEmail. I have further added a validator to my form group and the form controls have required validations attached.

My use case is: I want to trigger the custom validation based on the value of the individual form controls and if the custom validator returns as invalid, mark the form controls as having errors.

My issue is that: Once the form controls are marked with errors, custom validator (which is on form group) does not trigger until I touch both of the form controls again and change the values for both of them. I would ideally want to just update one of the fields (to match the other one) and be able to trigger the custom validation again.

Sample code:

@Component({...})
export class FormComponent implements OnInit {
  user: FormGroup;
  constructor(private fb: FormBuilder) {}
  ngOnInit() {
    this.user = this.fb.group({
      data: this.fb.group({
        email: ['', Validators.required],
        confirmEmail: ['', Validators.required]
      }, , { validator: validateEmail })
    });
  }

  const validateEmail = (control: AbstractControl): {[key: string]: boolean} => {
    const email = control.get('email');
    const confirmEmail = control.get('confirmEmail');

    if (email.value === confirmEmail.value) {
          return null;
     } else {
       this.setFieldAsInvalid(email);
       this.setFieldAsInvalid(confirmEmail);
       return { nomatch: true };
     }
  };

  private setFieldAsInvalid(field: AbstractControl) {
      field.setErrors({
        'invalid': true
      });
   }
}

Any suggestions to fix this or any other approach I should take.


Solution

  • You are changing controls(email and confirmEmail) into invalid when they keep different values, but return null in ValidatorFun will only change current formcontrol's valid to true, not all email and confirmEmail.


    Solution1: You can change email and confirmEmail back to valid via field.setErrors(null) when they keep same value.

    if (email.value === confirmEmail.value) {
      // clear custom error for all related controls
      this.setFieldAsValid(email);
      this.setFieldAsValid(confirmEmail);
      return null;
    } else {
      this.setFieldAsInvalid(email);
      this.setFieldAsInvalid(confirmEmail);
      return { nomatch: true };
    }
    
    
    private setFieldAsValid(field: AbstractControl) {
      field.setErrors(null);
    }
    

    see fixed demo.


    Solution2: If you only care about error nomatch, you can track on formgroup data to see whether it has error of nomatch like below(also included in above demo's template part):

    this.user.get('data').hasError('nomatch')     
    

    Mention that This way will leave the other formcontrol to be invalid.