How to create a generic error handler with Angular?

Earlier in this newsletter, we covered how to handle errors with RxJs Observables. What about errors in the rest of the application? Javascript has a primary try/catch mechanism, but this doesn’t help handle errors in a generic, unified way.

Fortunately enough, Angular has an ErrorHandler interface that can be implemented by global error handler services such as this one:

That way, we can implement a catch-all method to deal with errors and display them to the user consistently (code example here). Even better, such a service could report errors to a server for further investigation and debugging from the dev team.

Here is a link to my custom error-handling tutorial to see a good example of how to implement a ErroHandler service.

Signals: effect()

After talking about how to create Signals, how to update them, and how to derive a Signal value from other Signals, let’s look at how we can register side effects out of any number of Signals.

Enter effect(). The behavior of effect() is almost the same as computed(), with one major difference: computed() returns a new Signal, whereas effect() doesn’t return anything.

As a result, effect() is suitable for debugging, logging information, or running some code that doesn’t need to update another Signal:

You can see that code in action here on Stackblitz.

Note that trying to update a Signal within an effect() is not allowed by default, as it could trigger unwanted behavior (infinite loop such as SignalA triggers update of SignalB that updates SignalC that updates SignalA – and around we go).

That said, if you know what you’re doing and are 100% sure that you won’t trigger an infinite loop, you can override that setting with the optional parameter allowSignalWrites as follows:

Anti-pattern: Not using production builds to deploy production code

This is one of the most common mistakes I see with my training/consulting clients. When deploying code to production, they would use the command: ng build.

Instead, you want to use: ng build --configuration=production

Why is that? Because a production build is optimized in several ways:

  1. The code gets minified and obfuscated, which means it looks like this when running in a browser:

This code is as lightweight as possible (no tabs, whitespace, new line characters, variables have super short names, etc.) and a lot more challenging to understand (a hacker would have a harder time understanding your code).

2. The code gets tree-shaked. Angular removes unused dependencies and dead code and makes your build output as tiny as possible. Size matters on the web: The less code you ship to a browser, the faster it gets downloaded, parsed, and interpreted (which is also why Angular gives us lazy-loading capabilities)

3. Source maps are not generated in that same spirit of hiding what our source code looks like.

4. Angular DevTools are disabled on that code, again for obfuscation and reverse-engineering purposes.

If you’re still not convinced after reading all of this, give it a try on your Angular projects. The size of your dist folder after a production build should be at least 90 to 95% smaller compared to a regular build, which is massive.

HostBinding and HostListener

A few months back, I suggested that Angular developers don’t use enough directives and use too many components. This is because components quickly become second nature when writing Angular code, while directives are less common and, as a result, can feel more complex at first.

The critical difference between components and directives is that components have an HTML template, and directives don’t. That lack of a template can be slightly uncomfortable at first unless you realize that you still have access to HTML attribute bindings but in a different manner.

For instance, let’s consider the following component template:

Those two bindings used in a directive would become:

In other words, when you see a binding with [] in a component, @HostBinding() does the same thing in a directive.

For example: [id]="testId" in a component template becomes @HostBinding("id") testId; in a directive class.

The same goes for event listeners. This component (click) binding:

Becomes the following in a directive:

As a recap: [] become @HostBinding and () become @HostListener. Those are the same thing. That’s it. No template is needed. For a real-life example of a custom directive, feel free to take a look at this tutorial of mine.

Signals: computed()

After introducing how to create Signals and how to update them, let’s take a look at one more exciting feature that helps replace the need for RxJs Observables.

How to emit a new Signal value when one or more Signals get updated? That’s what computed() does. In my Signals course, I illustrate computed() with the following example:

In the above code, this.rates() and this.currency() are two different Signals. this.rates() emits up-to-date exchange rates for all currencies in the world. this.currency() emits the current currency selected by the user.

computed() takes a function as a parameter. The function returns the computed value from my two Signals; in this case, the up-to-date exchange rate for the current currency. If the exchange rates or the currency get updated, this computed Signal will emit an updated value automatically.

This is somewhat similar to combining several Observables and using switchMap or combineLatest to get a customized result. It’s a lot easier with Signals (one line of code!).

Anti-pattern series: Using too many services

While services and dependency injection are good, just like many good things in life, they can be over-used and become somewhat of an anti-pattern.

Why? Because of how change detection works in Angular. Angular has two modes for change detection: The default mode and the onPush mode. onPush is an optimization that works if and only if you’re using input-driven components, also known as presentation components.

In other words, whenever you inject a service into a component, you prevent that component from using the optimized onPush change detection mode. This is one of the reasons best practices recommend sticking with container components (service-driven components tied to specific use cases) and presentation components (input-driven ones that can be used and reused as they’re not connected to any service – and any business logic).

The more presentation components you create, the more reusable your code is, and the more change detection can be improved. The next time you inject a service in a new component, think again: Could I pass that data as @Input(s) instead of injecting a service?

If so, congratulations: You just prevented your presentation component from falling into this anti-pattern.

Skeleton loaders with Angular

Skeleton loaders are grey-shaded shapes that indicate when a part of the screen is loading. Here is an example of skeleton loaders for Facebook:

ngx-skeleton-loader is a small component library that does just that. It gives us access to different skeleton loaders ready to be used in our applications:

What’s nice about that library is that we can customize different aspects of the skeletons, such as animations and colors. You can find some live examples here.

If you want to explore skeleton loaders for your application, feel free to look at my popular tutorial: How to use a skeleton loader with Angular?

The tutorial has several code examples that can be used as-is.

Two ways to update Angular Signals

Yesterday, I wrote about some best practices around exposing a Signal in our Angular applications. Let’s now take a look at the two different ways a Signal can be updated.

set()

The easiest way to update a Signal is the set() method. Nice and easy for basic data types such as strings or booleans:

update()

When the new value of a Signal depends on its previous value, update() is the best method to use. This is the ideal method for a counter, for instance:

Here is how to increment the counter based on its current value:

TL;DR

  • When you need to update a simple value (string, number, boolean), use set().
  • If that new value is based on the previous one, use update() instead of set()

Anti-pattern series: Not unsubscribing from Observables

We covered this in our newsletter before: Observables can cause memory leaks if we don’t unsubscribe from them.

Before Angular 16, there were a few different techniques available to unsubscribe automatically, the best of those being the async pipe (and yes, it’s possible to use the async pipe 100% of the time if you use the tricks highlighted here – no excuses).

With Angular 16, things are even better, as you can use the new takeUntilDestroyed operator as follows:

But here’s my million-dollar tip for today: Instead of using takeUntilDestroyed in your components, use it in the services that expose such Observables:

That way, whether you use an async pipe or not, your components are covered. That’s one of the nice things about Observables: We can change them whenever and wherever we want using pipe(), including in our services – so that all subscribers benefit from that change downstream.