Angular validation errors made easy

Luca Micieli
The Startup
Published in
4 min readMay 21, 2019

Angular reactive forms are great. Dinamicity, validation and binding make me - and part of the community with me - prefer them over template driven ones. At the end of the day, however, either being reactive or template driven,
a form should let an application collect data from the users and provide
them feedback about data validation.

We think that both strategies suffer from a poor way of showing error messages to the users. In this article, I will write about how we tried to solve this problem in xtream.

Validation errors verbosity

How do we show validation errors for an input? As the bible (aka Angular documentation) suggests, we have to write something like this

https://angular.io/guide/form-validation#built-in-validators

You can simplify this template by adding a getter in your component:

and the validation becomes:

https://angular.io/guide/form-validation#built-in-validators

This is a form containing just one field with four different validation types and we already end up with 20 lines of code. The template-driven situation is not very far from this as well: you have to write a set of if statements, at least one for each validation that a field requires, and show the specific message.

While working on sofan, after the first landing page in late august that required name, surname, email, birth date, city, and phone number plus some consensus, we started searching for a more efficient and dynamic way to show validation error messages in forms.

Requirements

In Utopia we would just set up validators while creating the FormControls and a language specific message based on the error type would appear under the input. The message should be different per field and form, and possibly parametric, so that if we change a validator from Validators.min(4) to Validators.min(5) the message changes accordingly. Moreover, for some generic errors like required we do not want to copy and paste “This field is required” everywhere. Instead, we would like to have a fallback to a general one (different per validation type) if the form specific one is not present.

Doing so we can focus on validation logic inside the component and only care about messages in language files, avoiding to pollute the template with those horrible ngIf clauses.

After some googling and brainstorming we created a small library that covers all the requirements.

@xtream/ngx-validation-errors

The final solution is a set of components that allow us to write a form template that appears clean and short:

Final usage of @xtream/ngx-validation-errors

Let’s see how ValidationContextComponent , FormFieldContainerComponent and InputErrorsComponent work to accomplish what we need.

InputErrorsComponent

This component accepts a set of error messages together with parameters and shows them translated using @ngx-translate . Messages are just translations keys like “NEW_HERO.NAME.ERRORS.MINLENGTH” and parameters are a JavaScript object enriching error info, such us {minlength: 8, actuallength: 6} , so we can use {{messageKey | translate:params}} . We will see shortly how this component is added into the DOM. Input error appears as a red text under the input; using innerValidationError property of validationContext you can move errors inside input in case, for example of a dark background.

Errors showed inside input with innerValidationError set to true

ValidationContexComponent

The responsibility of this component is to query for all the inner FormFieldContainers and set into them the validation context passed as input. In the example above is “NEW_HERO”. This allow to use error messages that are specific per form.

FormFieldContainerComponent

This component wraps the input in a container so that we can dynamically add or remove the errors component under it. This is the template.

Container component to wrap input and add errors

Using content projection we add at the bottom of the input the InputErrorsComponent passing error messages and parameters.

The FormFieldContainerComponent then does the magic:

The component selects the FormControlName in its content using @ContentChild. If the form control is invalid, dirty and touched it creates the list of current errors using the template

${validationContext}.${fieldName}.ERRORS.${errorType}

thus transforming field name and errors type in SCREAMING_SNAKE_CASE. The error type is identified using the keys in the errors object as well as the message parameters (you can directly look at builtin validators here, but it works also using custom ones).

If the generated key is not present in the translations the message fallbacks to a configurable default validation context (such as “GENERAL”) removing field name: “NEW_HERO.NAME.ERRORS.MINLENGTH” becomes “GENERAL.ERRORS.MINLENGTH” so you can keep some base errors general.

At the end if the component is able to read the native html element to which the form control is linked, it toggles also the class is-invalid that you can use to mark the input with a red border, for example.

Library usage

We know the way errors are shown and the base validation context are opinioned. While extracting the validation components into a reusable library we exploited the forRoot static method for configuration.

First of all install it using:

npm i @xtream/ngx-validation-errors

In your app.module.ts add:

Simple library import

Now you can use validationContext and formFieldContainer in your templates.

To customise general context and errors component use

Customised library import

The errorsComponent must respect the below interface so that it can be substituted with the default one.

Remember to add it to the entryComponents list otherwise it can not be instantiated dynamically using component factory.

You can find the whole Angular workspace with library and demo in the github repo. The first alpha release of the library is available on the npm registry under the name @xtream/ngx-validation-errors.

It’s an ongoing work, so if you find bugs or you think that something
is missing please open a issue or submit a PR.

Thank you for reading this article!

Share any comment and ask any question here or get in touch through my LinkedIn profile.

--

--

Luca Micieli
The Startup

Front-end developer and Co-Founder @xtream. Angular, React, and TypeScript lover.