More RxJS Fun

February 18, 2022

More RxJS Fun

I recently wrote a post titled “Playing with RxJS in Angular” where I discussed observables, subscribing to observables, Angular’s async pipe, and more. Today will be a continuation of that post where I’ll discuss more RxJS features. Let’s go ahead and jump right in.

Get Todos by ID

Let’s say we have an array of todo item id’s, and we want to get the todo from the jsonplaceholder api for each one. We could do the following:

Create an array of ITodo to hold our retrieved todo items:

todos: ITodo[] = [];

Create an array of the todo id’s:

todoItems = [
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
  ];

Create a getTodo method in the service, which is responsible for getting one todo based on id:

getTodo(todoItem: number) {
    return this.httpClient.get<ITodo>(`${this.todosUrl}/${todoItem}`);
  }

Call the getTodo method that we just defined in the service from the app.component.ts file and push each retrieved todo in to the todos array:

private getTodos() {
    this.todoIds.forEach((todoId) => {
      this.appService.getTodo(todoId).subscribe((todo) => this.todos.push(todo));
    });
  }

Now, let’s update the template so that we can see the todos:

<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>

There’s nothing here that I didn’t discuss in my last post. The only difference is we are now getting todo items by their ID.

The of Operator

With the of operator we can convert our array of todo id’s in to an observable sequence:

ofTodoIds = of(...this.todoIds);

The ofTodoIds property is an observable of numbers. This means we can subscribe to ofTodoIds. This also means we can use the pipe function and play with more RxJS operators. To learn more about the of operator, check out this article.

Now we can update the getTodos method in the app.component.ts file to use the new ofTodoIds observable:

  private getTodos() {
    this.ofTodoIds.subscribe((todoId) => this.appService.getTodo(todoId).subscribe((todo) => this.todos.push(todo)));
  }

The map Operator

Each of our todo values has a title property, and we are mapping that to a field in the template with the label “Todo Name:“. Each todo item looks like this when we receive it from the server:

{
  "userId": 1,
  "id": 20,
  "title": "ullam nobis libero sapiente ad optio sint",
  "completed": true
}

Here’s how the todo items look in the UI.

lowercase todo titles

I would like to capitalize each letter of the title in each of the todos. This can be accomplished with a little help from the map operator. The map operator is used to transform each item in an observable.

Let’s have a look:

  private getTodos() {
    this.ofTodoIds.subscribe((todoId) =>
      this.appService
        .getTodo(todoId)
        .pipe(map((todo) => this.capitalizeTodoTitle(todo)))
        .subscribe((todo) => this.todos.push(todo))
    );
  }

Whoaaa, this looks strange, right?! What is this pipe thing? The pipe operator was introduced wayyyy back in RxJS version 5.5, and it allows developers to compose multiple operators inside of it. Prior to the pipe operator, operators were chained together using dot notation. You can read more about the old way vs the new way here.

Inside of the pipe operator you’ll see the map operator. I’m passing in the todo to a helper method I created called capitalizeTodoTitle, which basically does exactly that, it capitalizes each word in the title of an ITodo object. The method looks like this:

  private capitalizeTodoTitle(todoToChange: ITodo): ITodo {
    return {
      ...todoToChange,
      title: todoToChange.title
        .split(" ")
        .map((word) => word[0].toUpperCase() + word.substring(1))
        .join(" "),
    };
  }

Now, if we look at the UI we’ll see that each word in the title of each Todo item is capitalized.

uppercase todo titles

Woooo! We’ve covered quite a bit, but let’s keep going.

The concat Operator

Let’s create another Observable of todo Id’s, similar to the ofTodoIds property that we created earlier.

First, I’ll create a regular ol’ array of numbers like so:

todoIdsTwo = [21, 22, 23, 24, 25];

Now, I’ll use the of operator and create an Observable using the array we just created.

ofTodoIdsTwo = of(...this.todoIdsTwo);

Now we have two observables that will stream todo id’s. What if I want to get the todos using both? I could make the call to the backend twice… or I could combine the observable streams in to one using the concat operator. Let’s go with the concat operator option. That will look something like this:

const allTodoItems = concat(this.ofTodoIds, this.ofTodoIdsTwo);

Now, we can use the new allTodoItems observable in the getTodos method:

  private getTodos() {
    const allTodoItems = concat(this.ofTodoIds, this.ofTodoIdsTwo);

    allTodoItems.subscribe((todoId) =>
      this.appService
        .getTodo(todoId)
        .pipe(map((todo) => this.capitalizeTodoTitle(todo)))
        .subscribe((todo) => this.todos.push(todo))
    );
  }

The concat operator is easy to use, and saved us quite a headache. Essentially what it is doing is subscribing to the first observable and streaming those, once the first observable is finished, the operator will then move on to the next and start streaming its values.

That’s all for this post

I’ll cover more RxJS operators in future posts. If you want to see the code for this post in full, you can view the repo here. Thanks for reading.


Profile picture

Written by Jason Fritsche.

Designed and built by Jason Fritsche