Codementor Events

Angular Validators With Conditional Validation In Reactive Forms

Published Nov 22, 2018Last updated May 20, 2019
Angular Validators With Conditional Validation In Reactive Forms

Form is an essential part of building a robust and scalable Angular application. Validating your application form before reaching out to the server enhances security and performance.
In this post, I will be showing you how to use validators in Angular reactive forms; we would also leverage on the validators to implement conditional validations in reactive forms.

Below are basically what you will learn at the end of this tutorial:

  • How to create basic Angular forms
  • How to use Angular validators with Angular reactive forms
  • How to implement conditional validations based on certain values in the form.

Without further ado, let's get started!

Creating a new Angular Application

To get started, we have to create a new Angular application using the powerful Angular cli — make sure you have it set up locally.

To create a new project, simply type the following command

ng new angular-reactive-validation

And that is it! Angular-cli will automatically bootstrap the project in a folder named angular-reactive-validation.

Creating The Angular Reactive Form

We will be using the existing app.component.ts file for our component logic; feel free to use anywhere you deem fit in your application.

Creating the form group

There are different ways to create a reactive form. My preferred approach is using the FormGroup alongside the FormBuilder. The idea is still the same even if you choose to use the FormControl or any other approach — many ways to the market you know.

First thing first, let's import the FormBuilder, the FormGroup, the Validators will be using soon, and then create two fields for working with our form:

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

form: FormGroup;
formSubmitted = false;
  • The form variable will hold the form we will be creating soon
  • The formSubmitted holds the status of our form: whether it has been submitted or not, defaulted to false.

Next up!
We inject the FormBuilder in the class constructor.

constructor(private formBuilder: FormBuilder) {}

Let's build our form inside a method named buildForm:

buildForm() {
    this.form = this.formBuilder.group({
      email: [null],
      username: [null],
      userCategory: ['employee'],
      institution: [null],
      company: [null],
      salary: [null],
    });
  }

That's all we need to add to our app.component.ts file for now, but let's briefly explain what we have done:
We used the FormBuilder injected into the constructor to build the form and assigned the value to this.form field that we declared in the class earlier.

The Submit

When our form is submitted, we create the onSubmit method to handle it:

onSubmit(event) {
    event.preventDefault();
    this.formSubmitted = true;

    if (this.form.valid) {
      console.log(this.form.value); // Process your form
    }
  }

There is nothing much going on here. When the form is submitted, we set the this.formSubmitted field to true; we then check if the form is valid before logging it to the console — in reality though, you process the form and send it to the server.

The Component Template

Open the app.component.html and add the following code:

<h3 class="text-center">User Details</h3>
<hr>
<form [formGroup]="form" (ngSubmit)="onSubmit($event)">
  <div>
    <label for="email">Email</label>
    <input type="email" id="email" formControlName="email" placeholder="Enter email"/>
  </div>
  <div>
    <label for="username">Username</label>
    <input type="text" id="username" formControlName="username"  placeholder="Username"/>
  </div>
  <div>
    <input type="radio" formControlName="userCategory" id="userCategory2" value="employee"/> Employee
    <input type="radio" formControlName="userCategory" id="userCategory1" value="student"/> Student
  </div>
  <div>
    <label for="institution">Institution </label>
    <input type="text" id="institution" formControlName="institution" placeholder="Enter institution"/>
  </div>
  <div>
    <label for="company">Company</label>
    <input type="text" id="company" formControlName="company" placeholder="Enter company"/>
  </div>
  <div>
    <label for="salary">Salary</label>
    <input type="text" id="salary" formControlName="salary" placeholder="Enter salary"/>
  </div>
  <button type="submit" class="btn btn-primary">Submit</button>
</form>

We have added the formGroup directive to the form tag and also formControlName to each form input. We also binded our onSubmit method to the form's ngSubmit event. This is all that is needed to start implementing the reactive form validations.

Let's go back to our component class!

Adding Validation

We will be using the Angular built-in validators for the form validations — the one we imported earlier:

buildForm() {
    this.form = this.formBuilder.group({
      email: [null, [Validators.required, Validators.email]],
      username: [null, [Validators.required]],
      userCategory: ['employee'],
      institution: [null, [Validators.required]],
      company: [null, [Validators.required]],
      salary: [null, [Validators.required]],
    });
  }

As you can see, all the form fields are required except the userCategory, which of course is a radio button with default value of employee.

Let's call the buildForm method in our class ngOnInit method:

ngOnInit() {
    this.buildForm();
  }

Updating the Component Template

After adding validation to our reactive form fields, we need to make some adjustment to the component template. Add [ngClass]="{'form-submitted': formSubmitted}" to the each of the template form inputs. Something like this:

<input type="text" id="username" formControlName="username" [ngClass]="{'form-submitted': formSubmitted}" placeholder="Username"/>

With this ngClass directive, we just asked Angular to add the class form-submitted to the input whenever the formSubmitted field in our class is true. Literally, add the class form-submitted to the input field whenever the form has been submitted. We would use the class for styling in the next section.

Using CSS to highlight the invalid form fields

At this stage, we still can't see the effect of the validations we just implemented. We're going to need to write some css.

When working with Reactive forms, Angular automatically adds some classes to each form field. Each of these classes correspond to the state of the fields when the form just rendered and as the user interacts with them. These classes are thus:

  • ng-valid
  • ng-invalid
  • ng-touched
  • ng-untouched
  • ng-pristine
  • ng-pending
  • ng-dirty

You can know more about each of these classes here: angular form classes

Let's open the app.component.css and add the following css:

.ng-valid.ng-dirty:not(form) {
  border: solid 2px lightgreen;
}

.ng-invalid.ng-dirty:not(form), .ng-invalid:not(form).form-submitted {
  border: solid 2px lightcoral;
}

We are doing two things here:

  • We highlight the border of the form inputs with light green color, but only when the user has typed something in the field and is a valid one.
  • We highlight the border of the form inputs with light coral colour, but only when the user has typed something in the field and is an invalid input. We also make sure all the invalid fields are highlighted with same color once the form is submitted.

If we add some bootstrap styling, the form, after being submitted without values, should look similar to this:
Screenshot 2018-11-22 at 11.44.41 AM.png

And that is it. You have learned how to implement validations in Angular reactive form using validators.
Time to move on to conditional validation in Angular reactive forms — our next topic of the day.

Conditional Validation in Reactive Forms

So far so good, our form has been validated, but, let's say, that is not the desired result we want. Currently, the form is invalid until the user filled all the required fields.

We want our form to be valid for the following conditions:

  • When the user category is employee, the field institution should not be required: should be optional.
  • When the user category is student, the fields company and salary should not be required; they should be optional.

This means we have changes to make for user category as the user toggles between the two values (employee or student).

Let's get to business!

Setting Validation For User Category

Let's create a new method setUserCategoryValidators with the following code:

setUserCategoryValidators() {
    const institutionControl = this.form.get('institution');
    const companyControl = this.form.get('company');
    const salaryControl = this.form.get('salary');

    this.form.get('userCategory').valueChanges
      .subscribe(userCategory => {

        if (userCategory === 'student') {
          institutionControl.setValidators([Validators.required]);
          companyControl.setValidators(null);
          salaryControl.setValidators(null);
        }

        if (userCategory === 'employee') {
          institutionControl.setValidators(null);
          companyControl.setValidators([Validators.required]);
          salaryControl.setValidators([Validators.required]);
        }

        institutionControl.updateValueAndValidity();
        companyControl.updateValueAndValidity();
        salaryControl.updateValueAndValidity();
      });
  }

Let discuss what's happening here:

  • We start by getting the controls of the concerned fields we need to validate using our this.form and assigned them to local variables — for resusablity.
  • We then use our this.form again to get access to the userCategory form control and subscribe to its value changes.
  • Inside our subscribe callback, in the first if statement, we check whether the value is a student. If it is, We use the in-built Angular form control's setValidators method to add validation to the institution field. We also use same method to remove validation from company and salary fields, by setting them to null. This pattern is also used for the second if statement which is pretty much intuitive.
  • Lastly, we use the Angular form control's updateValueAndValidity method to update the fields. It is important to note here that calling setValidators method without calling updateValueAndValidity method will amount to no changes in the form. That is, no changes will be effected on the form and its validations. updateValueAndValidity is actually the method that informs Angular to effect the new changes made to the form control. Hence, always make sure it is called after calling setValidators on controls.

Calling the setUserCategoryValidators method in the ngOnInit should now look like this:

ngOnInit() {
    this.buildForm();
    this.setUserCategoryValidators();
  }

Updating Validations On The buildForm Method

One last thing to do is to update the form validations in the buildForm method:

buildForm() {
    this.form = this.formBuilder.group({
      email: [null, [Validators.required]],
      username: [null, [Validators.required]],
      userCategory: ['employee'],
      institution: [null],
      company: [null, [Validators.required]],
      salary: [null, [Validators.required]],
    });
  }

As can be seen, the user category is set to employee by default. We can then remove validation for the institution field. Our new setUserCategoryValidators will trigger the corresponding validation for institution, should the user choose student option for the user category, and vice versa.

Putting everything together, when a user chose student as user category and clicked submit, the highlighted fields should now look like this:

Screenshot 2018-11-22 at 2.12.37 PM.png

From the picture above, we can see that the salary and company fields are not highlighted, but the institution is highlighted with the other required fields. The salary and company fields are now optional.

Screenshot 2018-11-22 at 2.33.44 PM.png

As shown above, also, when the user switched to employee, the salary and company fields become highlighted, while the institution field becomes optional and therefore not highlighted.

Conclusion

In this tutorial, we are able to cover the following:

  • How to create basic Angular form using reactive approach
  • How to use Angular built-in validators with reactive forms
  • How to use css to highlight our invalid form fields
  • How to use Angular form control's setValidators and updateValueAndValidity methods to set conditional validations on form fields.

If you would like to see the full source code, visit the Github repo

I would love to hear your corrections, suggestions or feedback in the comment section. Thanks for your time!

Discover and read more posts from Jimoh Hadi
get started
post commentsBe the first to share your opinion
Balakrishna
2 years ago

I have form where it is having two fields from and to. I need to fire validation when from > to. For example I entered from value as 6 and to value as 5. Validation is firing as expected but if I change the from value 4 now the validation message is not clearing. I have tried to.setValidators(null) with updateValueAndValidity() it worked fine. But again if I change the to value as 3 then validation is not firing back. I have written two Custom validation methods and passing these methods to form control.
Could you please suggest the solution?

Satyaki Dey
4 years ago

Totally worked for me. Thanks.

Robert Rypuła
4 years ago

Great article! I have one question. How to show asterisk in the template only when control contains Validators.required in the validator array? My goal is to only base on Validator list and not on some custom made flags in the code

Show more replies