Angular 19 released next week!

This week, we get back to the new format of the newsletter. I’m posting a few essential articles to revisit, updates to know about, and one question to ponder:

Three short articles to revisit:

  • If you subscribe to Observables within another subscription, you’ll want to read this as it’s an anti-pattern. The solution is to use the switchMap operator or another equivalent, such as mergeMap.

Two Angular updates:

One question to think about:

Do your prefer Template driven forms or reactive? What about none of that? Have you ever considered that option?

ChangeDetectorRef.markForCheck()

The following method call is either something you’re either very familiar with or something you’ve never heard about: ChangeDetectorRef.markForCheck()

If you’ve never heard about it, congratulations! It means you’ve been using Angular in a way that doesn’t require you to mess with change detection, and that’s an excellent thing.

On the other end of the spectrum, if you see that method in many places in your code, your architecture might have a problem. Why is that? As we covered before, there are two types of change detection: Default and onPush.

When we use onPush, we’re telling Angular: “This is a component that relies on @Input values to display its data, so don’t bother re-rendering that component when @Input do not change”.

The thing is, sometimes people start using onPush full of good intentions. Then, they start injecting services into their component, and when the data in those services change, the component does not re-render… So, instead of disabling ChangeDetection.OnPush (or figuring out a better way to keep their component as a presentation component), they go to StackOverflow, where people say: Just use ChangeDetectorRef.markForCheck(), and problem solved! Here’s a typical screenshot of such a response:

That’s because ChangeDetectorRef.markForCheck() does exactly that: It’s a way to tell Angular that something changed in our component, so Angular must check and re-render that component as soon as possible. As you can guess, this is meant to be an exception and not the rule.

The main reason why we would use ChangeDetectorRef.markForCheck() is if we integrate a non-Angular friendly 3rd party library that interacts with the DOM and changes values that Angular cannot see because Angular doesn’t manage that DOM. In other scenarios, we should avoid using it and think about a better architecture.

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.

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.

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.

Anti-pattern series: Calling a method in a template

If you’re calling a method/function in the HTML template of any of your components, try the following experiment: Add a console.log("here") inside that method.

Then, interact with your application by clicking around, entering information in a form, etc. What you’ll see in your console is something like this:

Why is this happening? Angular’s change detection runs every time an event occurs in the browser. Zone.js tells Angular that “something happened” when we interact with an Angular application. Then, Angular checks its component tree for updates. If you’re using methods in your HTML templates, the only way for Angular to see if the output of that method has changed is to… call it again.

As a result, using methods in your HTML templates is not recommended. Doing so can impact performance negatively. Instead, use bindings to class properties as follows:

Before:

After:

Note that if you need to call a method/function to perform some formatting, you should create a custom pipe. Pipes are designed to run only when their input data changes, which means they are optimized for best performance by default.

You can take a look at an example here on Stackblitz. I added a console.log("here") inside my custom pipe to showcase that it runs only once:

Anti-pattern: Subscription within a subscription

Welcome to this new series on Angular anti-patterns. I intend to cover one anti-pattern per week for a few weeks, starting with one of the most common ones: Subscribing to an Observable within another Observable subscription. Here’s an example where we get a country and then the currency for that country:

Why is this a bad idea? Here are a few reasons:

  • getCurrencyForCountry() is never unsubscribed, which can lead to memory leaks.
  • When the country changes, the Observable that gets the previous country’s currency is not canceled. If that Observable emits again, this.currency will get overwritten.
  • Such nested callbacks are hard to read, and as a result, such code is more challenging to maintain.

Let’s imagine our component template is as follows:

Then in a scenario where getCurrencyForCountry() emits updates values every second, our component would end up displaying the following values, which become wrong very quickly:

You can take a look at that code here.

The solution to avoid such issues is to use the switchMap operator as follows:

The above code works fine because switchMap cancels the previous currency Observable as soon as a new country is emitted from service.getCountries(). No more memory leaks, and our two Observables are always in sync:

Here is a link to that same code example using the switchMap operator.