New control flow RFC

One of the major announcements on day 1 of ng-conf was the control flow request for comments (RFC).

We’re all familiar with ngFor, ngIf, ngSwitch, and their syntaxes can get tricky at times.

Now what if ngIf was replaced with:

And what if the new syntax supported much more than ngIf, such as else if and even readable else cases:

The goal of the Angular team is to introduce syntaxes that are closer to regular Javascript and remove the need for these structural directives.

for loops would look like this and even support a empty template. All current ngFor local variables would be available automatically with no need for variable aliasing, too:

Finally, ngSwitch would become the following:

Angular CLI would perform the syntax updates throughout your Angular code, so there is no need to worry about rewriting those templates. You can find all the reasons why the Angular team decided on these syntaxes compared to other options in the RFC. You’re, of course, welcome to add your comments and suggestions to the list.

What’s new in Angular 16.1?

Angular 16.1 was released just in time for ng-conf 2023 (side note: If you’re in Salt Lake City, feel free to come to meet me and say hi – you’ll get a sticker and a $20 coupon for my video courses or certification exams).

There are three new features in Angular 16.1:

Transform option for @Input()

We can now have a function that transforms an input value to make it fit the needs of a component. Say, for instance, your input value is an object, and you want to extract one property out of it or return a boolean value if it passes a given check; then transform is what you need:

This simple example (code here) illustrates what can be done with the new option.

Support for Typescript 5.1

There isn’t a ton of new things in Typescript 5.1, mostly that now we can have setters and getters that use different types and have functions that return undefined.

Support for fetch() in HttpClient

This is in developer preview. You can enable it in the configuration of your HttpClient as follows:

I’m not sure there are any benefits of using it right now, but it might open the door to integrations with other HTTP providers built on top of fetch. Wait and see.

In any case, I’ll report my takeaways from ng-conf in the newsletter during the next few days. Exciting days ahead!

How to improve performance with pure pipes

Earlier in this newsletter, we saw that calling a method in a component template is an anti-pattern. The antidote to that anti-pattern is to use a pure pipe.

By default, all pipes we use in Angular are pure. Custom pipes are also pure by default. The only way to make a pipe impure is to add the config option pure: false to its decorator:

What is a pure pipe?

A pure pipe is one that Angular will execute only when there is a pure change to its input value. It is automatically optimized for performance since it is executed only when needed.

A pure change is either a change to a primitive input value (such as string, number, or boolean), or a changed object reference (such as Date, Array, or Object).

In other words, if we consider the following use case:

Here are some examples of pure and impure changes in variables:

With all that information, we are now equipped to call a formatting function in a template by using a pipe (said function would be called in the transform method of the pipe). We know that the function would run only when the input value changes (purely), which allows us to decide when we want that pipe to be executed again by making either a pure or an impure change to the input value.

Finally, we can double-check that our pipe is working as expected using the Angular profiler to make sure that the pipe doesn’t run more often than expected.

RxJs and Signals interoperability

Angular 16 introduced several features related to the brand-new Angular Signals, which include two functions enabling RxJs interoperability.

toObservable()

The name says it all. toObservable() takes a Signal and returns its data as an Observable:

This new Observable gets updated every time the underlying Signal value is updated. We can subscribe to it using the async pipe, for instance (demo code here):

toSignal()

toSignal() does the opposite of toObservable(). It turns an Observable into a Signal. Since Signals are different from Observables (a Signal always has a value right from the start – an Observable does not), the default behavior of toObservable() is to return a Signal that supports undefined as a default value:

This can be changed by providing an initial value as follows:

ngFor local variables

We use the ngFor directive so often that it’s easy to forget or even ignore some of its most powerful features.

For instance, it’s fairly common to access the index of an item in the array we’re working with, but there are actually five more local variables than can be used:

  • index: number: The index of the current item in the iterable.
  • count: number: The length of the iterable.
  • first: boolean: True when the item is the first item in the iterable.
  • last: boolean: True when the item is the last item in the iterable.
  • even: boolean: True when the item has an even index in the iterable.
  • odd: boolean: True when the item has an odd index in the iterable.

The syntax to use those local variables is as follows – and you can use as many as you want in a single ngFor directive like so:

How to create a copy of anything in Javascript?

Modern Javascript developers are used to the following syntax to create a copy of an object:

let copy = {...object};Code language: JavaScript (javascript)

While this approach works fine, it creates a shallow copy of the object, not a fully-fledged clone that also duplicates nested objects and arrays.

As a result, Javascript developers have learned another trick to perform a deep copy:

let copy = JSON.parse(JSON.stringify(object));Code language: JavaScript (javascript)

The idea of this approach is to turn an object into a JSON string before parsing that string back into a brand-new object. It works, but it’s not elegant and looks like a hack.

As a result, a better approach has been added to the Javascript language: the structuredClone function. It’s a syntax improvement that creates deep clones efficiently:

let copy = structuredClone(object);Code language: JavaScript (javascript)

The function is supported across all recent major browsers as shown on Can I use:

If you need to support another browser, there is a polyfill implementation in core.js.

Using a loading template with ngrxLet

A while back, I wrote about how ngrxLet is an improved version of the async pipe. I also covered how to use skeleton loaders with Angular.

Today, let’s look at how we can use these two tools to display a “loading” template while an observable is waiting for data.

As a reminder, here is how ngrxLet can be used to track different observable events:

In the above code snippet, we receive the values emitted by the number$ observable in a variable n. We would receive any error in a variable e. We would receive the completion status (true or false) in a variable c.

Here is how we can pass a custom loading template to ngrxLet using a template reference variable:

Of course, the loading template can be customized with anything you want, including a skeleton loader or an animated gif image. That feature is called a suspense template, and an observable is in a suspense state until it emits its first event (next, error, or complete as covered here).

RxJs of() and from()

So far, we have covered several RxJs operators and information about Subjects, but what about Observables themselves?

There are a few utility functions that create Observables out of existing data. Such Observables immediately return the data and complete upon subscription.

Here is an example of such an Observable created with the of() function:

And an example created with the from() function:

The difference between of() and from() is that of() emits all the data right away in a single emission, whereas from() uses an array of items and emits them one by one with no delay between emissions.

In other words:

What’s the point of an Observable that just returns data instantly and does nothing asynchronous? First is testing. Being able to return to an Observable with hard-coded data is perfect for unit testing purposes as we can quickly validate different scenarios without using the actual HttpClient, for instance.

Second is caching data and returning it as an Observable (which can be done even more easily with a BehaviorSubject or ReplaySubject). For instance, say we have a service that retrieves a list of all countries in the world from a server. We know that that list doesn’t change every day, so we want to cache it so that if another component requests that data, instead of making another HTTP request, we return an Observable of that data:

Note the use of the tap operator to spy on the Observable, catch the data, and store it in our “cache” variable.

That way, the API is consistent and components always subscribe to an Observable, not knowing if the data comes from a server or a local cache.

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.