Support us .Net Basics C# SQL ASP.NET Aarvi MVC Slides C# Programs Subscribe Download

Dynamically adding or removing form control validators in reactive form

Suggested Videos
Part 11 - Loop through all form controls in formgroup in reactive form | Text | Slides
Part 12 - Move validation messages to the component class in reactive form | Text | Slides
Part 13 - Move validation logic to the component class in reactive form | Text | Slides

In this video we will discuss how to add or remove validators dynamically at runtime.


Let us understand this with an example. To start with, Phone filed is optional.

angular reactive forms dynamic validation


However, if we select "Phone" as the contact preference, then it should become a required field.

angular reactive forms dynamic required

So here is our requirement 
  • Add the "required" validator to the Phone form control when the user selects "Phone" as their contact preference
  • On the other hand, remove the "required" validator from the Phone form control, when the user selects "Email" as their contact preference
  • So on the "Phone" form control, we have to dynamically add or remove the required validator function
This can be very easily achieved using the following 3 functions
  • setValidators()
  • clearValidators()
  • updateValueAndValidity()
These methods are available in the AbstractControl class. Since FormControl inherits from AbstractControl, these methods are also available to FormControl class.

Here is the HTML

<!-- Notice the click event handler on both the radio buttons. When "Email"
radio button is clicked "email" string is passed to the event handler
function. Similarly, when "Phone" radio button is clicked "phone"
string is passed to the event handler function -->

<div class="form-group">
  <label class="col-md-2 control-label">Contact Preference</label>
  <div class="col-md-8">
    <label class="radio-inline">
      <input type="radio" value="email" formControlName="contactPreference"
              (click)="onContactPrefernceChange('email')">Email
    </label>
    <label class="radio-inline">
      <input type="radio" value="phone" formControlName="contactPreference"
              (click)="onContactPrefernceChange('phone')">Phone
    </label>
  </div>
</div>

<!-- Email input element -->
<div class="form-group" [ngClass]="{'has-error': formErrors.email}">
  <label class="col-sm-2 control-label" for="email">Email</label>
  <div class="col-sm-8">
    <input id="email" type="text" class="form-control"
            formControlName="email" (blur)="logValidationErrors()">
    <span class="help-block" *ngIf="formErrors.email">
      {{formErrors.email}}
    </span>
  </div>
</div>

<!-- Phone input element -->
<div class="form-group" [ngClass]="{'has-error': formErrors.phone}">
  <label class="col-sm-2 control-label" for="email">Phone</label>
  <div class="col-sm-8">
    <input id="phone" type="text" class="form-control"
            formControlName="phone" (blur)="logValidationErrors()">
    <span class="help-block" *ngIf="formErrors.phone">
      {{formErrors.phone}}
    </span>
  </div>
</div>

Component class code

// Include phone property
formErrors = {
  'fullName': '',
  'email': '',
  'phone': '',
  'skillName': '',
  'experienceInYears': '',
  'proficiency': ''
};

// Include required error message for phone form control
validationMessages = {
  'fullName': {
    'required': 'Full Name is required.',
    'minlength': 'Full Name must be greater than 2 characters',
    'maxlength': 'Full Name must be less than 10 characters.',
  },
  'email': {
    'required': 'Email is required.',
    'emailDomain': 'Email domian should be prgaimtech.com'
  },
  'phone': {
    'required': 'Phone is required.'
  },
  'skillName': {
    'required': 'Skill Name is required.',
  },
  'experienceInYears': {
    'required': 'Experience is required.',
  },
  'proficiency': {
    'required': 'Proficiency is required.',
  },
};

ngOnInit() {
  // Include FormControls for contactPreference, email & phone
  // contactPreference has email as the default value
  this.employeeForm = this.fb.group({
    fullName: ['', [Validators.required, Validators.minLength(2), Validators.maxLength(10)]],
    contactPreference: ['email'],
    email: ['', Validators.required],
    phone: [''],
    skills: this.fb.group({
      skillName: ['', Validators.required],
      experienceInYears: ['', Validators.required],
      proficiency: ['', Validators.required]
    }),
  });

  this.employeeForm.valueChanges.subscribe((data) => {
    this.logValidationErrors(this.employeeForm);
  });
}

// If the Selected Radio Button value is "phone", then add the
// required validator function otherwise remove it
onContactPrefernceChange(selectedValue: string) {
  const phoneFormControl = this.employeeForm.get('phone');
  if (selectedValue === 'phone') {
    phoneFormControl.setValidators(Validators.required);
  } else {
    phoneFormControl.clearValidators();
  }
  phoneFormControl.updateValueAndValidity();
}

We can also achieve the same thing by subscribing to the valueChanges observable of contactPreference radio button in code, instead of binding to the click event in the HTML. The benefit of this approach is that, our code is easier to unit test.

Here are the steps

Step 1 : In the HTML remove click event binding from both the radio buttons (email and phone)

<div class="form-group">
  <label class="col-md-2 control-label">Contact Preference</label>
  <div class="col-md-8">
    <label class="radio-inline">
      <input type="radio" value="email" formControlName="contactPreference">Email
    </label>
    <label class="radio-inline">
      <input type="radio" value="phone" formControlName="contactPreference">Phone
    </label>
  </div>
</div>

Step 2 : Subscribe to contactPreference form control valueChanges observable

this.employeeForm.get('contactPreference')
                 .valueChanges.subscribe((data: string) => {
  this.onContactPrefernceChange(data);
});

angular 6 tutorial for beginners

2 comments:

  1. sir i want validation on drop down box values. the values is from api
    i was try with same as your video bt i get setvalidator null error can u help me

    ReplyDelete
  2. Wrong function. please refer this block:

    onContactPrefernceChange(selectedValue: string) {
    const phoneFormControl = this.employeeForm.get('phone');
    const emailFormControl = this.employeeForm.get('email');

    if (selectedValue === 'phone') {
    phoneFormControl.setValidators(Validators.required);
    emailFormControl.clearValidators();
    } else {
    phoneFormControl.clearValidators();
    emailFormControl.setValidators(Validators.required);
    }
    phoneFormControl.updateValueAndValidity();
    emailFormControl.updateValueAndValidity();
    }

    ReplyDelete

It would be great if you can help share these free resources