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?

Code challenge #3 solution

Before I dive into the solution of code challenge #3, I wanted to mention that my next FREE workshop is scheduled for April 4th. It will be all about getting into RxJs from scratch, and you can register here. I’ll start from the basics and move on to more advanced topics, such as subjects and operators, and you’ll be able to ask me questions as we go through the content and exercises together.

As always, it’s a free workshop, but your coffee donations are always welcome.

Let’s get into our code challenge solution. The goal was to “connect” two different dropdowns so that the selection of the first dropdown (a continent) would update the contents of the second dropdown (a list of countries for that continent):

The challenge is that countries depend on two different Observable data sources:

  1. An HTTP request to an API to get the list of all countries in the world
  2. The currently selected continent (a reactive form select dropdown)

In other words, we start with the following code:

Our goal is to combine data from both Observables into one result. Ideally, such a result would be updated whenever any of the Observable sources are updated (e.g., a new continent is selected by the user, or a new list of countries is available from the API).

As a result, we can use the withLatestFrom operator to make that “connection” between our two Observables:

Such an operator returns an array of all the latest values of our source Observables in the order in which they were declared. As a result, we can use the map operator to receive that array and turn it into something different:

Inside that operator, we decide to return a new array. The continent remains the same, and we filter the list of countries based on that continent:

FInally, we can use tap to run some side effects. In our case, we update the list of countries used by the dropdown, and we set the country dropdown value to the first country of that list to ensure that a country from a previous continent doesn’t remain selected:

And that’s it! You can see that code in action on Stackblitz here.

A few of you sent me their own solutions to the challenge, and a recurring theme was that most of you didn’t “connect” the Observables and just assumed that the list of countries would already be downloaded and available by the time the user selected a continent. Something along those lines, where this.countries would be received from the API before that code runs:

While the above code would work most of the time, if a user selects a continent before this.countries is defined, an error would be thrown and the corresponding subscription destroyed, which means the above code would not run anymore when the user selects a new value. As a result, using operators is safer.

Another trick in my solution is that I didn’t use any .subscribe in my code, which means I don’t have to unsubscribe, as I let the async pipe do so for me automatically:

That’s it for code challenge #3! Next week, I’ll send you updates from ng-conf in Salt Lake City. If you’re around, feel ree to come say hi!

Code challenge #3: RxJs Wizardry

It’s been a few months since our last code challenge, where the goal is to have you look at some code that isn’t working and make you improve your Angular skills along the way.

In this code challenge #3, we have two select dropdowns that have to work together so that when we select a continent, we can only see countries from that continent in the second dropdown. The current code isn’t working – Argentina isn’t in Europe, so this selection should not be possible:

You can fork that code from Stackblitz here. Feel free to email me your solution once you have one ready. I’ll send you a possible solution in next week’s newsletter.

In case you missed my previous code challenges, here they are, along with the link to their respective solutions:

ShareReplay Code Challenge #1 Solution

What was wrong with yesterday’s code example? Here it is as a reminder:

Since the developer used the shareReplay operator, the intent was to cache the result of the HTTP request so that we do not make that request repeatedly.

The problem is that if several components call the getData() method, they all get a brand new Observable since the method returns a new request every time, which defeats the purpose of using shareReplay.

How can we fix this? By creating the Observable once then sharing the results in subsequent calls to getData():

Here’s another way to do it, perhaps even more readable:

Why do these solutions work? DataService is a singleton, which means all components use the same instance of DataService. As a result, any property of DataService (such as cache$) is the same for all components that access it.

Another critical thing to note is that HTTP requests are cold observables, which means they only fire when a subscriber subscribes to them.

RxJs Timer for recurring tasks

You’re probably familiar with setTimeout (to run some code after a given timeout) and setInterval (to run some code at a given time interval). Both are “native” Javascript functions and can be used with Angular.

That said, recurring code execution is asynchronous, and asynchronous work is often done with RxJs in Angular apps. As a result, let’s talk about a 100% RxJs way to replace setTimeout and setInterval.

The RxJS timer operator creates an observable that emits a value after a specified delay. The delay can be specified in milliseconds or as a Date object.

The following code creates an observable that emits a value (0) after one second:

If we want timer to emit immediately and then keep emitting at a given interval, we can do the following:

This would emit 0 immediately, then 1 five seconds later, 2 ten seconds later, etc. Most of the time, we don’t care about the emitted number. We want to turn that Observable into another one (like an HTTP request, for instance) using our good friend switchMap:

What’s the benefit of using timer in such scenarios? We get automatic unsubscriptions if we use the async pipe or the takeUntilDestroyed operator.

Here is a tutorial for a concrete example of how to do HTTP polling with Angular and the timer operator.

Use functions for readable RxJs operator chains

When you have complex RxJs operator chains, a good idea is to refactor them into simple functions. This can also favor code reuse for simple scenarios such as the following example.

Say we have a User object with the following shape:

We receive that object through an Observable; all we want from it is the user’s age. Using Rxjs (and date-fns), we could do something like this:

This works, but it’s not instantly apparent that we’re computing the age of the user, and if we need similar code elsewhere, we’d have to copy-paste that line of code, which is terrible.

Instead, we could create a function with an explicit name:

And then use it as our new custom operator function:

We can improve the signature of our function for type safety purposes, too:

You can see that code in action on Stackblitz here.

Obviously, the approach makes even more sense when you have several operators, and you can even combine such operator functions with other operator functions:

Now we have beautiful, maintainable code that can be read without documentation.

RxJs retry() operator

Some weeks ago, I posted about how to handle errors in RxJs. In some cases, it might be a good idea to retry an Observable when it fails, especially for HTTP requests.

The retry operator has the following marble diagram:

In the above marble diagram, we retry the Observable 2 times. Since all attempts fail (the X in the timeline indicates an error), we still end up with an error at the end, but we can see that the first and second errors were invisible to the subscriber thanks to the retry operator.

The nice thing about retry is that we can specify a delay before retrying, as well as how many times to try again. By default, if we don’t use any parameter, retry will keep trying forever:

In the above example, we would retry a maximum of 3 times, wait 5 seconds between each attempt, and reset our retry counter if the Observable ends up succeeding. That way, another request in the future would still get all 3 attempts, for instance.

If you’re interested in a slightly more complex example using several more operators, I wrote that tutorial “How to do polling with RxJs and Angular?” that uses the retry operator.

Error Handling in RxJs

A few days ago, we saw that we could be notified of any error when subscribing using a specific callback function or that we could receive the same information using the tap operator.

There is another option available using a specialized operator called catchError and illustrated below under its deprecated name catch:

How does catchError help? As we can see in the marble diagram, it can return a different Observable when an error happens. This means that the subscriber would have no idea that something wrong happened as we are able to switch to a backup solution (perhaps some cached data or a different API endpoint).

This is especially important when you know that if an Observable throws an error, it will never emit another value in the future. This makes catchError an excellent option to recover or at least attempt to recover from an error in the browser. We will cover other options (such as operators that retry an Observable) in the subsequent editions of the newsletter.

RxJs TapObserver

In my last post, I covered how we can define multiple callback functions when subscribing to an Observable (next, error, complete). I mentioned that those callbacks are available when we call the subscribe() method, which goes against our philosophy of avoiding subscriptions as much as possible because they can lead to memory leaks.

In previous newsletters, I covered how the tap operator can help avoid subscriptions by “spying” on an Observable.

Well, tap is a simple gift that keeps on giving. The operator supports an TapObserver interface that can be used instead of the callback function we used in our earlier examples:

As a result, we can register several side effects in our code based on any of these notifications as follows:

Most of these notifications are explicit in what they do, but let’s document all six of them for the sake of clarity:

  • next: Invoked whenever the Observable receives new data
  • error: Invoked whenever an error occurs within the Observable
  • complete: Invoked when the Observable completes and stops emitting any new values.
  • subscribe: Invoked whenever a new subscriber subscribes to the Observable
  • unsubscribe: Invoked whenever a subscriber unsubscribes from the Observable
  • finalize: Invoked whenever the Observable is done, either because of an error, of completion, or unsubscription. It’s like a catch-all notification to mark the end of an Observable.

You can see an example of such TapObserver in action here on Stackblitz.

mergeMap RxJs operator

A few months back, we covered the switchMap operator from RxJs. Today, let’s look at a similar operator called mergeMap:

The marble diagram isn’t as obvious as it is for other operators, so let’s clarify what mergeMap does:

  • It takes values from an observable and maps them to another observable
  • Then it allows us to combine the values from both observables into one new observable

What’s a real-life use case for mergeMap?

Let’s say you want to display a list of products with their associated categories. The product information is stored in one API endpoint, and the category information is stored in another API endpoint. To display the list of products with their categories, you need to make a separate API call for each product to retrieve its category information.

You can use mergeMap to combine the two API calls and merge the results into a single stream of data (product + category).

How is it different from switchMap?

mergeMap differs from switchMap in two important ways:

  1. switchMap only returns the data from observable #2 – mergeMap returns data from both observable #1 and #2 and allows us to combine that data any way we want
  2. switchMap cancels observable #2 whenever observable #1 emits a new value, then re-creates another observable #2. mergeMap doesn’t do that.

In other words, switchMap is perfect when we want to chain asynchronous actions such as “user selects a new filter, then we request new data according to that filter” because if the user changes that filter again, we want to cancel the previous request and start a new one.