Angular 17: New esbuild builder with Vite

Who doesn’t like faster builds? Since Angular 2, the Angular team has consistently worked on making our builds quicker and faster. Angular 16 included a developer preview of a new build system based on esbuild and Vite.

With Angular 17, this feature is now enabled by default for new apps, with reported speed improvements of over 67% in most applications.

To enable this new build system in existing apps (that use v16+), open your angular.json file and look for all the places where @angular-devkit/build-angular:browser is used:

Then, replace those instances with @angular-devkit/build-angular:browser-esbuild

And your next run of ng serve or ng build will automatically use this new build system. Nice and easy! If you use server-side rendering or have tweaked the builders in the past, you’ll want to look at the extra instructions listed on this page.

Angular 17: Trigger options for @defer

Yesterday, we introduced the new @defer bloc to lazy-load components on the screen. We saw that @defer includes several config options to display errors, placeholder, and loading templates.

Today, let’s focus on the possible triggers for such lazy loading.

idle

This is the default option. Will load the contents of that block once the browser is in an idle state:

viewport

Triggers the deferred block when its content enters the viewport. For instance, that would be when the user scrolls down, and the block becomes “visible.” Note that this option requires a placeholder (used to detect when the element comes into the viewport):

Another interesting option is to load the deferred block when another specified element makes it into the viewport using a template reference variable. That option does not require a placeholder:

interaction

Waits for the user to interact with the placeholder to load the deferred block. Even better, this trigger can be combined with another element so you can lazy-load a component on a button click, for instance:

The above code can be tested on Stackblitz here.

hover

Similar to the two previous examples. Waits for the user to hover over the placeholder or a specified element:

immediate and timer

immediate triggers the deferred block immediately as soon as Angular has finished rendering. Timer waits for a specified delay:

You can see most of these different examples on Stackblitz here. There are a few more options for @defer that I’ll cover later to keep this newsletter short and readable. It’s incredible what such a small API can do!

Angular 17: Lazy-loading with @defer

Angular 17 is available, and we already covered its new optional control-flow syntax. Today, let’s cover the new option to fully customize lazy-loading with Angular using @defer.

What’s great about @defer is that it does not rely on the Angular router anymore. You can lazy-load any standalone component anywhere, anytime, and on your terms, as you can decide the trigger to load that component.

Let’s start with a basic example:

The above code will load the HelloComponent as soon as the browser is idle, which means it’s done loading everything else. While that is happening, we can decide to display a placeholder, and in my case, I decided that such a placeholder would be displayed for a minimum of 2 seconds no matter what. You can use seconds (s) or milliseconds (ms) as a time unit:

You can see the above code in action here on Stackblitz. Note that the Angular Language service was updated accordingly, so VS Code (Stackblitz is a web-based VS code), Webstorm, and other IDEs already know about the new @defer syntax and do proper highlighting of it!

We can also specify a loading template and an error template, all customizable with a minimum amount of time during which we would display those templates (example on Stackblitz here):

I used “big” numbers to see the different states in my examples. Note that @loading supports an “after ” option only to show the loading template if loading takes more than a certain amount of time, so you don’t have to display anything if the component loads quickly. Both parameters are optional:

These are the different new blocks available with @defer. Tomorrow, we’ll look at the various possible triggers.

The Angular renaissance is on!

With a new logo and a new website released just before a new version (v17 will be released on Wednesday, November 8th), Angular is more than ever in the middle of a remarkable transformation:

There is a lot to unpack from yesterday’s live event (you can watch a recording here), and I will cover all these updates over the next few days, starting today with the new home for Angular: angular.dev

The website is a complete rewrite of all contents and tutorials from the Angular team and will replace angular.io soon. I have played around with it a little bit, and I find the site to be incredibly fast, especially the new search feature accessible from the left menu and the ctrl + K command:

The content is also straightforward and easier to navigate than angular.io. It’s an absolute pleasure looking for information and finding it almost instantly with lots of examples and great info:

There’s also a new playground with different examples to play with. You can edit the code on the website and see the result live, just like with Stackblitz:

Tomorrow, I’ll start getting into the main updates of v17, and there’s a lot to unpack there as well! In the meantime, I already got the new logo on my laptop:

What do you think about it? Note that the colors are not set in stone, and the actual logo on the website is a gradient that’s constantly changing colors from a primary palette outlined here:

More info about the new Angular branding can be found here with downloadable logo samples.

Dependency Injection: ElementRef

We use dependency injection daily to inject services in our Angular “objects,” such as components, directives, pipes, and other services.

Angular does offer other kinds of injectables, though, and one of those is the ElementRef. Its primary purpose is to give us direct access to the native DOM element of a component or a directive.

Here’s a code example (you can test it in Stackblitz here):

The above code changes the background color of the HTML element to red. It’s important to note that the above implementation is not ideal. We should use Angular data bindings for such a use case, which are a lot more readable (code example in Stackblitz here):

The Angular team doesn’t recommend using this API either:

So when should we use ElementRef? Just like markForCheck, it’s a last resort option, and we should use it when everything else fails. The only use case I would consider for ElementRef is when I have to interact with a non-Angular 3rd-party library, such as a chart or a map library, after carefully checking that Angular wrappers are not available on npm (or such wrappers do not pass my tests for good, reliable dependencies)

RxJs eslint plugin for Angular

Last month, I discussed eslint and how to use that tool to parse your code and receive feedback on best practices and possible improvements.

Today, I want to mention an eslint plugin explicitly written for RxJs with Angular: eslint-plugin-rxjs-angular.

This plugin adds three possible validation rules:

All rules are optional, and it doesn’t make sense to use all of them at once because these best practices are contradictory in the sense that the goal is for you to choose one of these three approaches and be 100% consistent with it:

  1. The first rule will enforce that you always use the async pipe in your components.
  2. The second rule doesn’t care about the async pipe but wants to ensure you unsubscribe on destroy.
  3. The third rule is the most specific, as it enforces that you always use a Subject with takeUntil to unsubscribe in your components.

I’d suggest using the first rule only because we covered before that the async pipe is the safest and most performant option. And remember that you can always use the async pipe. There are no excuses!

Special Angular Event Next Week

If you follow the Angular team on social media (Youtube channel or Twitter/X), you probably saw that the team scheduled a special live event for next week. The team went as far as changing their logo on these channels to tease us:

In the past few months, we have all noticed what has become known as the renaissance of the framework, with new features such as standalone components, Signals or the new control flow API that is happening next week with the release of Angular 17.

All these changes embody the spirit of making Angular easier to learn by removing the need to know about some of the most difficult concepts (ngModules, RxJs, change detection, etc.) when getting started. All I can say is that it’s going to be a great week to be an Angular developer!

Click here to get notified when the live event starts on Youtube.

Async pipe, Signals, and Change Detection

Yesterday, we covered what markForCheck() can do in terms of change detection and why it could be needed at times.

Regarding best practices, I often mention using the async pipe or Signals to improve change detection and performance in our apps. Why is that? That’s because both the async pipe and Signals have some extra code to help Angular detect changes.

Here’s what we can find in the source code of the async pipe:

The last line of code indicates that whenever an async pipe receives a new value, it marks the corresponding view for check, triggering the need for change detection. That’s the reason why the async pipe works well even when used in an OnPush component: Its internals override the behavior of whatever change detection strategy we use.

Signals do something similar by marking their consumers (or “subscribers,” in a sense) as dirty when the Signal value changes:

As a result, and back to the point I was making yesterday, Instead of trying to use markForCheck() directly in our code, we should rely on the tools designed to do it automatically and in an optimized fashion for us.

Conclusion: Always use the async pipe with RxJs (you can always do so) or use Signals for an even better experience.

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.

Perf and template syntax – Example 3

Yesterday, we looked at the pros and cons of our second code example.

Today, let’s cover the pros and cons of our third and final example:

Example #3 – Signal

<div *ngFor="let data of dataSignal()">{{data.name}}</div>

This last example is very interesting for a few reasons. First, if you follow Angular best practices, you might be screaming that we’re calling a method in a template, and you would discard that code immediately.

But the thing is, this is a Signal, and Signals are different. It’s perfectly fine to call a Signal in a template, and as a matter of fact, this is the way to do it so Angular knows where Signals are read and can then optimize DOM updates based on that information.

Even better, when we use a signal with ngFor, the subscriber to that signal is actually a view (an instance of ng-template) and not the entire component. When Signal-based components become part of the framework, Angular will be able to re-render just that view instead of the entire HTML template of that component.

What are the cons, then?

The only downside of Signals is that they require Angular v16+, so you’ll need to upgrade your apps to use them. The other downside is that the full benefits of improved change detection are not there yet and won’t land in v17 either, but the Angular team is making steady progress, and some early parts of Signal-based components were released in version 16.2.

Other than that, Signals are the way to go. if you’re just getting started with Angular and building a new app, I believe you should use Signals as much as possible.