Angular 2 Pipes — Error | Messages

I spent the last 3 days at the very excellent Angular (2+) Master Class by thoughtram, a big thank you to Christoph Burgdorf and Thomas Burleson who ran it. If you want to know more about the course, maybe start with Glen’s Day 1 Retro. This will be about Pipes (formerly Filters in Angular 1) and how to use them to convert ngForm error objects into nice validation messages. The same technique will work with Filters if you’re on Angular 1.

What are Pipes?

Pipes perform transformations. There’s the DatePipe that transforms Date objects into a readable string, e.g. new Date() becomes Jan 12, 2017. There’s the AsyncPipe that transforms an Observable or Promise into it’s (last) result (or null if it’s pending) — which is seriously powerful, check it out!

The bad news is that there is no FilterPipe or SortByPipe in Angular 2+.

The ErrorsPipe

I wanted to create a Pipe that would convert all the various ngForm errors into pretty validation messages. This is what I wanted to be able to do in the template:

The field name was the only thing I felt it needed to pass in. Note that the ngIf could check firstname.invalid, but it doesn’t need to — the ErrorsPipe should return null or an empty array if the control is valid.

The errors field is either null for no errors, or an object with a field for each error. So that means check for null, then iterate over the fields (using Object.keys) and return appropriate messages.

So here’s the ErrorsPipe:

While the required field is ‘true’, minlength and maxlength are objects that each have a requiredLength field (and an actualLength field) that we can use to create a richer validation message. The default case in the switch is useful if we haven’t handled a particular validator — or we’re happy with a generic message.

This version doesn’t handle duplicate messages, but it could very easily get rid of them using a unique/distinct utility methods, like _.uniq().

I can see this method working really well if you have to handle multiple languages. Maybe inject a translation service? Or what about delegating to a TranslationPipe instead?

Thoughts? Questions? Comments? Let me know.