Conditional validation in Angular 2

Viewed 1662

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.

4 Answers

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.

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