简体   繁体   中英

Conditional validation in Angular 2

Validation in Angular 2 is pretty straight forward which is awesome. However, how would one go about making a required field optional if some other field is selected.

Here are my rules

this.contractsFilter = this.fb.group({
  selectedContractType: ['', [Validators.required]],
  selectedYear: ['', [Validators.required]],
  selectedPde: ['', [Validators.required]],
  refNo: ['', []]
});

I want other fields flagged as required to be 'un-required' if the refNo is provided by the user.

Here is how you can dynamically add or remove validator to some control

private addValidator(control, validator) {
    let existingValidators = control.validator;
    control.setValidators(Validators.compose([existingValidators, validator]));
    control.updateValueAndValidity();
  }

And to Remove validator

  private removeRequiredValidator(control, otherValidators) {
    control.clearValidators();
    if (otherValidators) {
      control.setValidators(otherValidators);
    }
    control.updateValueAndValidity();
  }

Both the use of custom validators and setValidators can get very complex very quickly. I prefer to configure the validators as they may be required and then conditionally enable or disable them.

Using exactly your code:

this.contractsFilter = this.fb.group({
  selectedContractType: ['', [Validators.required]],
  selectedYear: ['', [Validators.required]],
  selectedPde: ['', [Validators.required]],
  refNo: ['', []]
});

I would then subscribe to valueChanges for refNo :

this.contractsFilter.controls['refNo'].valueChanges.subscribe(value => {
  if (value) { // There is a refNo specified
    this.contractsFilter.controls['selectedContractType'].disable();
    this.contractsFilter.controls['selectedYear'].disable();
    this.contractsFilter.controls['selectedPde'].disable();
  } else {
    this.contractsFilter.controls['selectedContractType'].enable();
    this.contractsFilter.controls['selectedYear'].enable();
    this.contractsFilter.controls['selectedPde'].enable();
  }
});

Now your validators are all specified in one consistent place, and you can still control whether the validators are executed or not.

Make use of setValidators to set/unset validators for form controls

import {FormControl, Validators, FormGroup} from "@angular/forms";

onRefNoEntered() {
        if(this.contractsFilter.controls['refNo'].value && this.contractsFilter.controls['refNo'].valid) {
            this.contractsFilter.controls['selectedContractType'].setValidators(null);
            this.contractsFilter.controls['selectedYear'].setValidators(null);
            this.contractsFilter.controls['selectedPde'].setValidators(null);
        }
        else {
            this.contractsFilter.controls['selectedContractType'].setValidators(Validators.required);
            this.contractsFilter.controls['selectedYear'].setValidators(Validators.required);
            this.contractsFilter.controls['selectedPde'].setValidators(Validators.required);
        }
}

I faced a similar issue and, inspired by this solution , made something like this:

const control = new FormControl(null, 
    [this.conditionalValidator(Validators.required)], 
    [this.conditionalAsyncValidator(this.validatorService.customAsyncValidator(params))] );

private conditionalValidator(validator: ValidatorFn): ValidatorFn {
    return (control: AbstractControl): ValidationErrors  => {
        if(this.condition) {
          return null;
        }
        return validator(control);
    }
}

private conditionalAsyncValidator(validator: AsyncValidatorFn): AsyncValidatorFn {
    return (control: AbstractControl): Observable<ValidationErrors> | Promise<ValidationErrors> => {
        if(this.condition2) {
            return observableOf(null);
        }
        return validator(control);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM