22
votes

I want to apply conditional validation on some properties based on some other form values. I have referred some answers Angular2: Conditional required validation, but those are not fulfil my need. Because I have to implement conditional validation in 40+ form(around 30 fields) of my large enterprise application. I don't want write the same code in every component and change the FormControl name. I don't know how this can be achieved via Directive.

if age control valuev is greater than 18 than the license number field is required.

Here's my code:

this.userCustomForm = this.angularFormBuilder.group({
age:['',Validators.required],
licenseNo:[''] // Here I want to apply conditional required validation.
});

In my application there are some cases where I want set conditional validation based on nested FormGroup or FormArray values.

Please guide me, how can I achieve this.

5
Maybe this approach could work? - Robby Cornelissen
I want to apply conditional required validation on my licenseNo field based on the value of age. - vinit tyagi

5 Answers

45
votes

For me it worked perfectly like this:

this.userCustomForm.get('age').valueChanges.subscribe(val => {
  if (condition) {
    this.userCustomForm.controls['licenseNo'].setValidators([Validators.required]);
  } else {
    this.userCustomForm.controls['licenseNo'].clearValidators();
  }
  this.userCustomForm.controls['licenseNo'].updateValueAndValidity();
});

You have to updateValueAndValidity of the form for the changes to take effect.

Hope this helps!

17
votes

My suggestion would be to use dynamic validations.

Subscribe to the changes in the age field of the userCustomForm and whenever the age reaches the condition where license needs to validated, add validators.required dynamically using setValidators() and clear the validators dynamically using clearValidators() whenever necessary.

    this.userCustomForm.get('age').valueChanges.subscribe(val => {
        if (condition) { // for setting validations
          this.userCustomForm.get('licenseNo').setValidators(Validators.required);
        } 
        if (condition) { // for clearing validations
          this.userCustomForm.get('licenseNo').clearValidators();
        }
        this.userCustomForm.get('licenseNo').updateValueAndValidity();
    });
3
votes

There is a more generic approch which can be use for multiple purpose, not just this one.

Each time you want to conditionally add the Validators.required to a control you can use this function.

First create this function (in a service should be the best idea because it's generic, so you can use it later with different conditions in a different component, but for the example it's in the same component)

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

conditionallyRequiredValidator(masterControlLabel: string, operator: string, conditionalValue: any, slaveControlLabel: string) {
  return (group: FormGroup): {[key: string]: any} => {
    const masterControl = group.controls[masterControlLabel];
    const slaveControl = group.controls[slaveControlLabel];     
    if (eval(`'${masterControl.value}' ${operator} '${conditionalValue}'`)) { 
      return Validators.required(slaveControl)
    }
    slaveControl.setErrors(null); 
    return null;
  }
}

masterControlLabel: the control which will conditionally add the Validators.required to the slaveControl

operator: the operator you want to use to compare the masterControl value with the conditionalValue

conditionalValue: what ever the value you want the masterControl value must match to conditionally add the validator to the slaveControl

slaveControlLabel: the control which will receive (or not) the conditonal Validators.required

Second and finally, add the validator parameter (or validators parameter if mutliple validations need to be done) in your formGroup like this :

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

constructor(
  private angularFormBuilder: FormBuilder,
){ }

myFormGroup: FormGroup = this.angularFormBuilder.group({
  age: ['', Validators.required],
  licenceNo: [''],
}, {validator: conditionallyRequiredValidator('age', '>=', 18, 'licenceNo')});

In this example if the age is strictly over or egal to 18 then the licenceNo control will be conditionally required

1
votes

Conditionally set validator via setValidators method of FormControl class, ie

this.userCustomForm = this.angularFormBuilder.group({
    age:['', Validators.required],
    licenseNo:['']
});

if (condition) {
    this.userCustomForm.get('licenseNo').setValidators([
        Validators.required
    ]);
}
0
votes

I solved this problem by doing this :

this.userCustomForm = new FormGroup({
    age: new FormControl('',Validators.required)
    licenseNo: new FormControl('', condition ? Validators.required : [])
});

There are another way to do this by using setValidators and clearValidators methods please see the following example:

if(condition) {
    this.userCustomForm.get('licenseNo').setValidators(Validators.required);
} else {
    this.userCustomForm.get('licenseNo').clearValidators();
}