Playing with RxJS in Angular
As an Angular Developer I work with RxJS pretty frequently. I am comfortable enough with RxJS to get my work done, but would not consider myself an expert. I’ve decided I’d dedicate some time to getting more familiar with RxJS and reactive programming concepts. I plan on starting slow, even going over RxJS operators, objects, concepts, etc that I’ve been using in order to gain a better understanding of reactive programming. Without further ado, let’s start.
Note: I am assuming you have some (don’t need to be an expert by any means) experience with Angular
Setup
I created a public repo that you can view or clone and play around with. Link is here. For the sake of simplicity, I have not created any extra modules and will be working in the app folder. I’ve created an interfaces folder and a services folder. The app.service.ts
file is where I’ll be making API calls to a few jsonplaceholder endpoints.
Subscribe
I have created the following function in the app.service
getTodos() {
return this.httpClient.get<ITodo[]>(`${this.todosUrl}`);
}
Now, if I want to make a call in the app.component
to retrieve all of the todos, I can do so like this:
private getTodos() {
this.appService.getTodos().subscribe((todos) => console.log(todos));
}
This getTodos
method is calling the getTodos
method in the appService
and subscribing to it. If we did not subscribe to this.appService.getTodos()
nothing would happen. Why is that? This is because the getTodos
method in the service returns an Observable, more precisely, it returns an Observable of ITodo[]
. Why does it return an Observable? This is due to using the get
method of the HttpClient
which returns an Observable of the HttpResponse. “An Observable represents a stream, or source of data that can arrive over time” - RxJS Primer. In order to tap in to the stream of data, we must subscribe to it, which is exactly what we are doing in the getTodos
method in app.component.ts
.
When we subscribe to an observable we can pass in a callback function and react to the data. In the case of getTodos
we are simply logging out the values we receive to the console. However, we could do whatever we want/need to with the values that are emitted from the observable.
If we wanted to show our todos in the template rather than logging them out to the console, we could just assign them to a property in the app.component
and then iterate over them in the template. That would look something like this:
We will store the todos in the todos
property in app.component.ts
:
todos: ITodo[] = [];
We can then update the getTodos
method in the app.component
to store the todos that we receive in the todos
array:
public getTodos() {
this.appService.getTodos().subscribe((todos) => (this.todos = todos));
}
Now, we can show them in the template by using an *ngFor loop:
<ul>
<li *ngFor="let todo of todos">
<h5>Todo Id: {{ todo.id }}</h5>
<h6>User Id: {{ todo.userId }}</h6>
<p>Todo Name: {{ todo.title }}</p>
<p>Completed: {{ todo.completed }}</p>
</li>
</ul>
And we should see the todos displayed.
Using the Async Pipe
An alternative way to display the todos would be using the async pipe. The async pipe subscribes to an observable and emits the latest values. We’ll have to make some changes in the app.component.ts
and app.component.html
files.
Let’s refactor the app.component.ts
file first. The first change is that I’ll update the todos property to be an Observable<ITodo[]>
:
todos$!: Observable<ITodo[]>;
The $ at the end is a convention that is sometimes used to convey that a property is an Observable. The ! is TypeScript’s non-null assertion operator, which we must use here because we haven’t assigned a value to the todos$
property yet.
The next change will be in the getTodos
method:
public getTodos() {
return this.appService.getTodos();
}
We will no longer subscribe to this.appService.getTodos()
. Instead, we will just return the call (which returns an Observable).
Next, in the ngOnInit lifecycle hook we will assign the getTodos()
call to our todos$
property.
ngOnInit() {
this.todos$ = this.appService.getTodos();
}
Now, since the todos$ property is an Observable, we can use the async pipe in the template.
<ul>
<li *ngFor="let todo of todos$ | async">
<h5>Todo Id: {{ todo.id }}</h5>
<h6>User Id: {{ todo.userId }}</h6>
<p>Todo Name: {{ todo.title }}</p>
<p>Completed: {{ todo.completed }}</p>
</li>
</ul>
Now we’ll see the todo items, this time with using the async pipe.
One of the great things about the async pipe is that it will automatically unsubscribe for you during ngOnDestroy.
That’s all for this post. I will continue with covering RxJS and Angular in coming posts.