Joshua Colvin

Adding dynamic validators in Angular

Published October 29, 2018

Adding dynamic validators to an Angular form control is not as straight forward as one would expect. We’ll explore some problems you may encounter and one possible solution.

The Scenario

Let’s say we have an input that always needs to validate the max length but is conditionally required based on some other setting in our application.

The Problem

We add the maxLength validator when the form is created:

this.form = this.fb.group({
  phone: ['', Validators.maxLength(10)],
})

And later we add the required validator using the setValidators() method:

this.form.get('phone').setValidators(Validators.required)

The required validation works but not maxLength.

Looking at the documentation for the setValidators() method, we can see why this occurs:

Calling this overwrites any existing sync validators.

So by calling setValidators() we overwrote the maxLength validator.

We need a way to add more validators to a form control so naturally we look at the AbstractControl documentation for a addValidators() method which sadly doesn’t exist.

The Solution

We can solve this problem by saving a reference* to any default validators our form control should have. Later we can pass the default validators to the setValidators() method along with any new validator(s) we want to add.

*Unfortunately there is currently not a way to get all the validators on a control (see this issue) so we will save a reference to the original validators. This is not ideal for large forms with lots of controls.

defaultPhoneValidators = [Validators.maxLength(10)];

if (this.phoneRequired) {
    this.form.get('phone')
        .setValidators([this.defaultPhoneValidators,
            Validators.required
        ]);
}

We’ve now added the required validator along with the existing maxLength validator.

At this point if we submit the form without the required value the form is still VALID. That’s because the control’s validity has not been updated. We must call the updateValueAndValidity() method after our call to setValidators() in order to recalculate the validation status of the control.

this.form.get('phone').updateValueAndValidity()

Now the phone control will be INVALID until it has a value.

Conclusion

As you can see, adding dynamic validators is possible with a bit of extra work. This would be simplified with the addition of either a getValidators() or addValidators() method.

You can see a working demo here

Find this article helpful or interesting? Follow me on Twitter for related content.

Joshua Colvin is a UI Software Engineer specializing in building component libraries. He lives with his wife and two kids in Michigan.