A generic Angular template-driven validator

profile
Tim Deschryver
timdeschryver.dev

A common complaint when it comes to creating custom validators with Angular template-driven forms is the boilerplate that one has to write before the validator can be used.

While I partly agree with that remark, it also has a clear separation of concerns because it adds a layer to validate the model. For the complex models, I take the opportunity to use the validator in a way that the validator is acting as a separate layer, containing the business rules.

The part where I do agree is that you have to touch (and review) multiple files to create and register the directive. The extra overhead (even if it's only a small effort) to add the validator directive to an Angular module is also often forgotten, which leads to frustrations.

But as we'll see, this doesn't have to be the case. We can create one validator to rule them all.

Let's simply call this validator ValidatorDirective, and all this directive does is accept a callback to a method that returns the validation errors.

With the ValidatorDirective we can now validate a form while defining inline validators. For example, to validate a single ngModel, we create the validator in the component. The validator (scoreValidator) receives the control and returns the validation errors, just like the validate method from the Validator interface.

To use the inline validator in the template, you assign the validator attribute (this is the selector from the ValidatorDirective directive) to the validate method (scoreValidator) .

Instead of having to write all the validators manually, you can also invoke the built-in Angular validators, or invoke your custom validators.

This is quick and easy for simple one-off validators.

Because validating a form group or even an entire form might get complex, it's a best practice to extract the validation rules outside of the component into its own method or class. This keeps the component small and simple, and makes the validation logic easier to test.

To validate a ngModelGroup you can reuse the same validator directive.

Note that I'm using the arrow syntax while declaring these validators. I do this to scope the method to the component class, instead of the directive class. This allows me to use other class properties within the validator method.

Revalidating the validator link

To make the validator react to another form value, we can use the registerOnValidatorChange method to revalidate the control. Each time a new value is set (via the value input), the control is revalidated. You can read more about this technique in my Template-Driven Forms guide.

To use the revalidation functionality, you assign the revalidator of the validator to a value that affects the validator. In the example below, the value of the password is passed to the password confirmation validator.

Conclusion link

We can eliminate some of the "boilerplate" by creating one generic validator directive that accepts a callback to validate a form model. This allows us to create inline validators within components. While this can be quick and easy for simple validations, I prefer to extract the complex validators into their own layer.

When the validation logic lives on its own (and not in a directive or in the component), it also doesn't bind the business rules to an Angular-specific layer.

Demo link

Incoming links

Outgoing links

Feel free to update this blog post on GitHub, thanks in advance!

Join My Newsletter (WIP)

Join my weekly newsletter to receive my latest blog posts and bits, directly in your inbox.

Support me

I appreciate it if you would support me if have you enjoyed this post and found it useful, thank you in advance.

Buy Me a Coffee at ko-fi.com PayPal logo

Share this post