Learning rxjs

Sean Konig
5 min readAug 17, 2020

--

Hey, I am Sean, and work with a very small team of developers. We run a web application that helps a business manage their everyday Health and Safety operations. Using Angular as the client-side framework gives you all sorts of helpful things right out the box and although it has n slight learning curve, it’s really not that bad to get your head around the main concepts of Single Page Applications.

According to the Angular documentation, the recommended technique for handling event data, asynchronous programming, and multiple values is Observables. In this article, we will look into how we use rxjs operators to interact with observables.

Okay, finally I can get to the point here. Using subscribe() is a really easy way to get our values from our observables. Once you read up on all of this you will learn that subscribing comes with its own problems. Scary words come up in articles. People mention things like ‘memory leaks’ and you start wondering if you really know what the fuck you’re doing.

Although it can all become very confusing I want to use a few real-world and simple examples of how I have implemented the rxjs library in our application. Note that this is just from what I have learned, if you feel I left something out then please comment and help me learn something.

anyway…

In this example, we are going to fetch a collection of answer types from our API and display the information in a drop-down box. We use the HttpClient Module provided to us by the Angular Framework. This call will be done from the component using a method in our service file. The HttpClient will return an observable that hold the values we requested from the api.

Your service will have something like this:

getAnswerTypes() {return this.http.get<any>(`${environment.apiUrl}/checklist/answer-types`);}

And we will call this method from our component doing this:

this.checklistService.getAnswerTypes()

This will now return an observable. To get the data we need to display on the front end we have two options.

Manually subscribe/unsubscribe

Option one is what we have been doing in our code up till now. You can use subscribe to get the result and then assign those values to a property on your component. This way the values are ready to use in your template once the request has completed. This approach does come at the small cost of unsubscribing in your component’s ngOnDestroy lifecycle hook. That feels like a lot of work for getting a simple collection and I can promise you that you or someone in your team is going to forget to unsubscribe from something as the application grows.

Async Pipe

Option two is to use the async pipe that Angular gives us. The async pipe takes care of the subscribing part and then returns the latest value emitted by the observable. The cool thing is that it automatically unsubscribes for us. This is perfect for our specific use case as we do not want to mutate any of the data received from the API, we simply want to pass this on to the template.

We can accomplish this by declaring our property as an Observable in our .ts file:

answerTypes$: Observable<ChecklistAnswerType[]>;

Now we can pass the return value of the http call (which if you remember is an observable) to this answer types property:

this.answerTypes$ = this.checklistService.getAnswerTypes().pipe(map((result) => result.data));

Let’s take a look at what is happening here.

The Pipe Function

As stated in the docs the pipe function is the assembly line from your observable data source. The Pipe Function allows you to declare a line of operators that your data set will run through. Think of it as declaring a recipe for your end result. In our example, we use the map operator to tap into the returned API data property from the result. If we wanted to perform any actions on these values we would “chain” more operators.

Operators

Operators give you the ability to manipulate the source data and return a new observable of the transformed value. That’s exactly what we are doing. We are receiving an observable, using the map operator to get the data property from the result, and then passing that as an observable to answerTypes$ which is now an observable containing the collection we asked for.

Now we can display our data using the async pipe as explained earlier:

<mat-option *ngFor="let answerType of answerTypes$ | async" [value]="answerType.id">{{ answerType.type }}
</mat-option>

Now we have no subscriptions that we might forget to unsubscribe from. These operators also offer is a nice clean way of mutating our data if we needed to. There are many operators at our disposal and trying to learn them all is a heavy task so I quickly want to mention a few that you will run into regularly.

The Map Operator

Map applies a given function to each source and returns the result of that as a new observable. To make this clear, we get a result from the API that looks like this:

message: "success",
data: [{id: 1, type: "Answer Type 1"}, {id: 2, type: "Answer Type 2"}]

We want to return just the data value and store it on our answerTypes$ property so we use the map operator to access the result and return the data property on it:

map((result) => result.data)

Nice and easy…

The Finalize Operator

Finalize will execute the given call back when the observable completes. So if you want something to happen after the data has been manipulated and passed on you can do that in a call back:

this.answerTypes$ = this.checklistService.getAnswerTypes().pipe(map((result) => result.data),
finalize(() => this.servicemanager.setLoading(false))
);

Now we have added a second operator to our recipe. Once the map operator has passed the result data we will now tell our service that the http request has been completed and we are no longer loading anything.

The Filter Operator

Filter will return the values that pass a certain condition. To explain this we will use filter to only return the data if the data property exists:

this.answerTypes$ = this.checklistService.getAnswerTypes().pipe(
filter(result => !!result.data),
map((result) => result.data),
finalize(() => this.servicemanager.setLoading(false))
);

Now our recipe also says that only if the data property is part of the result should we continue.

It’s not about not subscribing to observables. It’s about knowing when to subscribe to them. Sometimes it is absolutely necessary to subscribe to the results of an API call, when you post some data to the API, you would need to know whether or not that data has been dealt with or if an error occurred. That seems like a good time to subscribe to the result. There are of course ways to handle this re-actively, but this is going to depend on your situation and what actually needs to happen next in your code.

The Learn RxJS documentation is extremely helpful and easy to use. Operators are grouped by types and useful examples are given.

I really hope that this has been useful to someone. If you have any tips on how I can improve this article please let me know using the comments.

Happy coding everyone!

--

--