Reactive Angular

Pavel Tuzov
ITNEXT
Published in
9 min readJan 11, 2021

--

This post is about reactive programming in Angular. On a practical example, we will discuss how to implement maintainable business logic using RxJs.

It is expected that you have experience with Angular and minimal experience with RxJs (subscribing to Observables).

The demo will show top 20 HackerNews stories with the corresponding links to the articles, HN comments and a sentiment analysis of the title.

We will first see how you can use Observables and wrongly believe you are doing reactive programming. Then we’ll surely see how to actually be reactive.

The source code is surely in GitHub: reactive-angular

Reactive programming

Without transforming it into a theoretical article on reactive programming, here are shortly two notions describing it:

  • Asynchronous data streams
  • Propagation of change

We will treat everything (data) as streams, be it mouse clicks, http calls or even string values. We will be defining these streams and busying ourselves describing how we will react when the data comes in the future — how the change will be propagated through those streams.

source: https://gist.github.com/staltz/868e7e9bc2a7b8c1f754

In Object Oriented Programming, for example, we have objects and their corresponding state. The whole logic is written around manipulating this state of the objects.

In Reactive Programming, we have streams of data instead. We are writing the logic that transforms the data that flows through streams, until it reaches its final desired state. The state is not being stored until then.

Using RxJs the wrong way

You know that Angular’s HTTP client, for example, is using RxJs (Observable), so, whether you want it or not, you are already, at least, touching the reactive programming paradigm.

Let’s see how we can make bad use of it.

A bad thing you could do is make the http call in your component code. You shouldn’t. You write the technical stuff (http) in services. Component should contain only the presentation logic. But you know it already and you are creating services that implement HTTP APIs. So let’s create our service that implements the Hacker News API:

In order to get details about the top stories, we’ll have to make two HTTP requests: get the ids of the stories and get the details for each id.

Here is how you could do it in your component:

topNewsIds returns the Observable that will obtain the ids of top 20 news, when they come. And when they come, we will iterate through them and, for each, get another Observable, which will obtain the details of the news (when they come).

Although you are using RxJs here, you are not reactive. You are implementing something similar to callbacks. Moreover, in this very simple example, you already have nesting (and we’ve just started).

Now, what do you do if you decide that you want to show only stories (no job, ask or show)? You simply filter:

Are you reactive yet? No. You’re actually now on a track that leads to dark places. You are developing business logic in your component class. Because, you know, next comes the request to display the sentiment of the title — is it positive, negative or neutral news. How will you implement it? More nesting, more business logic in component? Will you still be able to keep track of what is going on there? What if you have a transient HTTP error when, say, getting story details? Will you be able to retry the request and continue?

Reacting

Using Observable (and Subjects), RxJs implements the observer pattern. Observable is our stream of data. But this is nothing new to you, right? Where RxJs gets cool, though, is its operators. That’s the propagation of change part of reactive programming.

Using RxJs operators you are describing your business logic. In a readable, maintainable way. You are building a recipe for what to do with data, when it comes, how to manipulate it until we get the desired form, the desired state of data.

Let’s, first of all, start building our business logic in a separate service, where it belongs. To make it simple in the beginning, we’ll refactor the getting story details part first:

In this minimalistic example, beautiful things are already happening. First of all, notice the pipe(). The pipe will describe the flow of data. You will define your business logic inside the pipe. The change will be propagated through this pipe and, using RxJs operators, you will describe how the data will be changed.

Inside the pipe you can see the first RxJs operator — filter. When the data enters the pipe, here is how it will be changed — only news with type story will go down the pipe. All the rest will be filtered out, i.e. no values will be emited.

Notice that the filter operator gets the story as input, not an Observable. Again, it will be called when the data comes, so the story will be available. For now it’s just a recipe. Also, notice that the result of filter (whatever gets returned by the story method) is an Observable. The return value of operators is always wrapped in an observable.

By the way, you usually want to have filter up in the pipe, s.t. irrelevant data is not unnecessary manipulated before it gets filtered out.

If it helps you to visualize what the operators do, there are marble diagrams for this. For example for filter.

And back in component:

What we obtained is, again, extracted the business logic out of the component. Not much is happening here yet, but now we’re ready for the future. Like, for example, let’s create a Data Transfer Object for the story:

And let’s transform whatever we get from the API to the model our application understands:

And here is the second one — map. Map can transform the structure of data. In this case we get the story object and transform it to News object. And an Observable<News> flows down the pipe. Since there are no more changes to be done, this is what gets out of the pipe and this is what the story method returns.

Now you’re familiar with the two very popular operators. How do you like the pipe? Is it readable? Did you notice how easy we implemented a new feature request (the News DTO)? It was pretty clear where the logic for this belongs. And you can continue to manipulate the data down the pipe, when next feature request comes, like getting the sentiment of the title…

I’m using Azure Cognitive Services to get the sentiment of a text. This is out of the scope of this post. The code is to be found in the cognitive.service.ts. You’ll have to add a cognitive.environment.ts file in the environments folder, that will contain the key and url. I surely didn’t include it in the repository. In the environment.ts you will see the structure of the cognitiveenv constant.

Having the CognitiveService, we can call its sentiment method (which, by the way, uses a pipe of its own) and get the sentiment as an Observable<string>. We could use again map to manipulate the structure of our data:

map(story => this.cognitiveSvc.sentiment(story.title))

But… The result of the sentiment method is an Observable<string>. And, remember, whatever gets out of the operator is wrapped in an Observable, so, in the end, we will obtain an Observable<Observable<string>>.

We have two problems here: outer Observable and the string. Let’s deal with Observable first.

There are, surely, flattening operators, that will flatten this structure into an Observable — exactly what we need. We will use mergeMap (or you will often find it by its alias — flatMap). There is an alternative — switchMap which has a small but important difference. Let’s leave understanding the difference as homework.

mergeMap(story => this.cognitiveSvc.sentiment(story.title))

Now, what will get out of the pipe will be an Observable<string>. String? What we need instead is update the story with the sentiment and send it down the pipe.

There are a couple of ways to solve this. One of them is defining a separate pipe for the sentiment stream, transform the story object using map and return the story object from that pipe (instead of simply the sentiment):

Easy to implement, we are already familiar with all the operators. The only thing I don’t like here is the nesting. But luckily, it’s still readable/maintainable at this point.

In order to avoid pipe nesting, we could use forkJoin to send both — sentiment and story down the pipe and then use a map to do that what we did in the nested pipe above. But, in my opinion, we would lose from understandability in such a pipe.

Wonderful! We keep adding features, by describing how we’ll react when the data goes through the pipe. The code keeps being readable and maintainable. The component has no idea about it and simply displays whatever stories it gets.

Now, what about those ids? Why does the component need to know how to get the ids and what to do about them? All it needs is to show top stories. That’s the logic it needs to implement.

We need to incorporate getting the ids in the business logic from the service — where it belongs. Here we have two very different options. The stream will either emit an array with 20 stories or it will emit each story one by one. I like the later approach for two reasons: we will not wait until we have the data for all stories and it fits well with the code that we already have (reacting to each id).

So, the idea is, when we get the array with ids, we will start sending them down the pipe one by one. Remember the mantra — everything is a stream?An array can also be a stream. from is one operator that can transform an array to stream. It will then emit the values one by one. Not to confuse with of operator, which will emit the entire array as single value.

Now our pipe begins with the ids and looks the same further down. This is the component’s code:

Look at this code. It does only that what it has to do — deal with the presentation logic. It gets data from the service and displays it. No business logic, no technical stuff (except disposing of subscription when the component is being destroyed).

Take a moment to understand what this news object is. This is that sought for final desired state. It’s our only state. The entire business logic is stateless.

Business logic is described in the pipe. This is where, in our example, reactive programming happens. Reactive programming ends when you call subscribe, when you get the desired state of the data.

Let’s push it further. Yet another feature request comes. We want a filter that will show only positive news. It already answers which RxJs operator you will use for this. Easy, right? You know exactly where this change belongs. This is a sign of a maintainable code.

Conclusion

There is a lot left to cover in terms of what RxJs can offer, like error handling, retrying (you’ve got it — there are operators for this that fit in the same programming style), merging streams and cold vs hot observables. But the main point of this post is to focus on how you can write maintainable code using RxJs.

RxJs fits nicely with Angular, as some of its modules are based on RxJs. Like, for example, the ActivatedRoute. Look at the component code which implements the positive news filter. The url fragment is also a stream. We don’t subscribe right away to it in order to get the state of the url fragment and then do something with it. Instead we describe how we will react when it changes. We will manipulate the fragment based on our needs, we will create some side effects (tap operator) by reseting the stories array and we will get the stories based on our filter. It all starts with the url fragment being changed and ends with the final news object. No state inbetween.

I wish, though, that Angular gets more reactive at its core (since they decided to go this path anyway). For example, when the filter checkbox value is changed, we are handling this event in a callback. Wouldn’t it be nice if we got the checkbox values as a stream? There are actually tricks to do it (using the fromEvent operator), but, in terms of Angular, it’s more of a workaround.

--

--

Senior Modern Apps Consultant Azure Cloud & AI @Microsoft | Full stack software developer