Search code examples
formsvalidationangularform-control

Angular 2 Cross Field Validation (model based) addErrors?


I am implementing a cross field validation for two fields in a form (reactive / model based approach) and do not know how should I add an error to existing Error List of a formcontrol

Form:

this.myForm = new FormGroup({
  name: new FormControl('', Validators.minLength(3));
  city: new FormGroup({
    cityOne: new FormControl('', Validators.minLength(3)),
    cityTwo: new FormControl('', Validators.minLength(3))
  }, this.validateEqualCities)
});

Validator:

validateEqualCities(formGroup: FormGroup) {
    return (control: AbstractControl): { [key: string]: any } => {
    if (formGroup.controls['cityOne'].value &&  formGroup.controls['cityTwo'].value && formGroup.controls['cityOne'].value !== formGroup.controls['cityTwo'].value) {

     formGroup.controls['cityOne'].setErrors({ 'equalCities': true }, { emitEvent: true });
     formGroup.controls['cityTwo'].setErrors({ 'equalCities': true }, { emitEvent: true });

        return { 'equalCities': false };

    } else {
      formGroup.controls['cityOne'].updateValueAndValidity({ onlySelf: true, emitEvent: false });
      formGroup.controls['cityTwo'].updateValueAndValidity({ onlySelf: true, emitEvent: false });
    }

    return null;
    };
  }

My Problem: If the validation fails "setErrors(..)" overrides all errors which are already there (Validators of formControls), so there is no correct state, because actually there should be 2 errors.

If I do not set errors to form controls directly and only return an error to the form, only the form is invalid and gets the error, but not its controls.

How can I achieve that both, form and controls has the real state of validation?

Thank you very much!


Solution

  • You can capture the errors object as it is before you assign the errors, modify it, and write the entire object back.

    validateEqualCities(formGroup: FormGroup) {
      return (control: AbstractControl): { [key: string]: any } => {
        if (formGroup.controls['cityOne'].value &&  formGroup.controls['cityTwo'].value && formGroup.controls['cityOne'].value !== formGroup.controls['cityTwo'].value) {
          let errors = formGroup.controls['cityOne'].errors ? formGroup.controls['cityOne'].errors : {};
          errors['equalCities'] = false;
          formGroup.controls['cityOne'].setErrors(errors, { emitEvent: true });
    
          errors = formGroup.controls['cityTwo'].errors ? formGroup.controls['cityTwo'].errors : {};
          errors['equalCities'] = false;
    
          formGroup.controls['cityTwo'].setErrors(errors, { emitEvent: true });
    
          return { 'equalCities': false };
        <...>
    

    Here's a Plunker with a working demo: http://plnkr.co/edit/XTeH1ifQTJSoMvBEvE0d?p=preview