Playing with RxJS in Angular

February 15, 2022

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.


Profile picture

Written by Jason Fritsche.

Designed and built by Jason Fritsche