This is a short summary on how to use a declarative pattern in Angular with RxJS.
If you are interested in the whole post, read RxJS Declarative Pattern in Angular on Medium.
To get the best out of this post, you should be familiar with Angular, RxJS, and observables.
RxJS doesn’t make your code declarative
Using RxJS doesn’t make your code declarative. Actually, as soon as you use the subscribe()
method, you are going imperative!
For this reason, we need to be declarative on purpose.
Step 1: Create the service
Let's start by creating a service where we declare a variable todo$
.
The dollar sign is a convention that indicates that the variable is an Observable and not the actual data.
The data coming from the url will be
- passed through a pipe
- passed through the RxJS tap operator
- saved in
todo$
However, if you just try this, nothing will happen!
You won't see anything in the console.
Step 2: Assign the observable to a local variable
Let’s move to the component.
The key line here is line 10, which is:
data$ = this.todoService.todo$;
We defined a local variable called data$
. We assign the observable from the service to the variable to make it available in the component.
Note that we are not telling Angular to subscribe or unsubscribe! We are not even seeing an observer! This is where the real declarative style shines.
In the end, we just want to display some data in the template. That is our goal. How Angular achieves it is not our business.
Once again, if you just try this, nothing will happen!
You won't see anything in the console on in the template.
This makes sense because we didn't tell Angular what our goal is!
Step 3: Use the local variable with async pipe
Let’s move to the template.
Here we tell Angular what needs to be done: Show the title of the object in the template when data is available.
To do that, we use the async pipe.
The async pipe automatically subscribes and unsubscribes, which is shown below:
// app.component.html
<div *ngIf="data$ | async as data">
{{ data.title }}
</div>
- First, we bind to
data$
from the component. - Second, we use the async pipe to automatically subscribe and unsubscribe to
data$
- Third, we use
as data
to define the variable that will host the value from the observable. - Finally, we can use data across the template as we did before.
A more comprehensive example and a comparison between this approach and the classic imperative approache is available on RxJS Declarative Pattern in Angular on Medium.
Let me hear your opinion!
Top comments (7)
if you want to do some manipulatios on the data you get from rhe observable, then how would you implement it the declerative way?
The same way you did in the service by using pipe() to apply an operator. For instance you could use map() to transform the output.
Ex: Return only the title:
title$ = this.todoService.todo$.pipe(map(data => data.title));
Thanks Cory! Nothing to add:)
How to do if you have list like
title$ = this.todoService.todo$.pipe(map(data : dataDTO[])=> data.find());
sorry for my english
Feel free to share some code example on StackBlitz!
One thing you should change is map(data : dataDTO[]). This is how the map operator works: vitainbeta.org/2022/03/29/hands-on...
By doing map(data : dataDTO[]) you are passing an argument that is not a function.
Good point! I could provide more example in another post but Cory's reply is the way to go!
From how it is written I am a fan of the declarative pattern but when I tried to unit test it I was somehow stuck. Can anyone refer an article on how to unit test todo$ in the example above? Any suggestions are welcome