Advanced Error Handling in @angular Forms

Part 2

Denis Severin
Fundamental Library
5 min readNov 14, 2022

--

Picture by Programmer’s Meme

In my previous article, I explained how we handle errors in @angular forms with the help of custom form containers and special structural directives.

In this article, I want to bring a topic of complex forms and grouping errors in one place to improve user experience. To better understand the topic, here’s a visualization of how the component works:

Recently we’ve released Message Popover. A component that wraps your forms with controls and renders form errors in one single place.

The task was to create a generic component that could be used not just with platform Form Container, but also with standard template-driven forms and reactive forms.

So what we had to achieve:

  • Support multiple forms for one message popover.
  • Support of different form types.
  • Support of error grouping by its type.
  • Support components such as Form Generator have form elements but are not visible to the Message Popover via ContentChildren.
  • Easy to use.
  • Render Message Popover component anywhere on the page yet keeping the context of the forms it listens to.

Getting projected forms

Since we support both form types, we search for ControlContainer, which is a base class for directives that contain multiple registered instances of NgControl. This way we get NgForm and FormGroupDirective with one query.

Getting form items

Let’s start with simple forms. We could use NgControl selector to get all controls inside the form, but we wouldn’t know additional data about the form item, for example, the human-readable control name.

So instead, we create separate directives which we apply to the control and pass such data to it:

And use it with our controls:

As you can see above, our directive exposes NgControl and also provides additional information such as its human-readable name. The benefit of this approach is that you can use any component with it, not even related to the @fundamental-ngx library itself.

Now, that we’ve found our forms and their controls with additional information, let’s define errors for it.

Defining errors

Since Message Popover can be used in almost any application and is not strictly bounded to the @fundamental-ngx library components, we added error configuration on the module level where users can define their messages per error:

Bear in mind that we are using our own i18n module for translating the strings. You can check it out by following this link.

In the example above we are passing the path to the localized string which supports interpolation. We are also passing error objects to the translation service so that developers can leverage this information in their error messages.

Now, with forms and their items defined, and also having error messages for each type of error, let's display them!

Collecting and displaying errors

For the Message Popover component we’ve decided to display and update errors on form submission. The reason is pretty simple: while the user edits some fields, he will definitely see errors for it next to the control itself, so no need for additional bindings.

So, let’s start listening for form submission. We also need to take care of the async validators which might complete after form submission:

Quick explanation here: we listen to all form submission events, and then switch to the stateChanges emitter, which will notify us whether the form is pending or not. If the form is not pending (e.g. all async validators completed), we check for errors.

You may notice that we use two different methods for collecting errors.

One is for the plain forms with fdpMessagePopoverFormItem items, and one is for the Form Container component which is more advanced, yet simpler for the developers to use while building forms.

Next, we collect errors and generate models to render:

Here we iterate over form controls and check whether it has directive applied. If not, skip it. Otherwise, generate an error message model based on the configuration, passed to the module with the error message configuration object.

So now that we’ve collected our errors, let’s render them in a fancy popover!

Rendering errors

Since Message Popover could be placed on any part of the page, we needed to split it into two parts: form wrapper and popover component. So to use the component developers need to use markup like that:

So the wrapper component searches for the errors and emits them, while message popover listens to them:

So, in the code above, we listen to the errors object change, and group it based on the error type, sorting it by the severity of the error, and then we filter it by applied error type.

Now that we have all parts together, I would like you to check the demo:

Now that we’ve covered simple forms scenario, let’s move to the more advanced approach.

Bounding Message Popover with Form Container

Before starting, I encourage you to read Part 1 of this topic where I explain how Form Container component works.

Since Message Popover already can work with reactive forms, no need for additional business logic to bind it to the form inside the form container.

Another thing is that Form Container has Form Fields, which support custom error messages, which we need to leverage. Remember I was saying that we have two methods for retrieving error messages? The second one is for components that are using Form Container to work with Form Field and its error messages.

Since Form Field error messages are structural directives, we need to work with template references of the messages:

In this long code snippet, we are collecting form fields that have errors, then, we try to get error message directives from them, and merge them with error messages from the module configuration. Additionaly, if the form container includes inner groups, we can take that group name and apply it to the whole group of controls it belongs to, thus grouping errors not only by the error type but also by the group name.

The source HTML code of a single error item looks like this:

Here we use conditional ng-templates based on the current error model. Whether it’s a plain string, or a structural directive, or another ng-template with its own error renderer.

Now that we’ve discovered how to connect message popover with form container, I’d like you to check out the demo:

--

--