RxJS in Angular: Demystifying RXJS And Its Use In Managing Asynchronous Operations

RxJS in Angular: Demystifying RXJS And Its Use In Managing Asynchronous Operations

If you are new to Angular (like me), and you have heard of RxJs, you might also be wondering what it really is and how to make use of this library in your Angular app. Well, I was like that too and I had to take my time learning what it is all about.

So let's dive in and see what this guy is...

RxJS (Reactive Extensions for JavaScript) is a library that provides a set of powerful tools for dealing with asynchronous and event-driven programming in Angular applications. At its core, RxJS revolves around Observables, allowing us to represent data streams and events over time. RxJS comes pre-installed with Angular, so you don't need to install it separately. You can start using RxJS directly in your Angular components and services.

Observables and Subscriptions

From the core of RxJS comes two important functionalities; Observables and Subscriptions. An Observable is a representation of a stream that emits data over time. Angular leverages Observables extensively, especially when dealing with HTTP requests, event handling, and state management. So basically, In order to receive the data emitted by an Observable, you need to subscribe to it. Subscribing to an Observable establishes a connection between the Observable and the observer (data consumer).

Now let's see a simple example of an Observable that emits a sequence of numbers:

import { Observable } from 'rxjs';

const numberObservable$ = new Observable<number>((observer) => {
  observer.next(1);
  observer.next(2);
  observer.next(3);
  observer.complete();
});

const subscription = numberObservable$.subscribe({
  next: (value) => console.log(value),
  complete: () => console.log('Observable completed.'),
});

// Output:
// 1
// 2
// 3
// Observable completed.

That's it! The code above is a simple demonstration of creating and subscribing to an Observable that emits a sequence of numbers and then logs those numbers to the console. Here's a step-by-step breakdown of the code and its output:

  1. We created an Observable called numberObservable$ using the Observable constructor. The Observable emits three values, 1, 2, and 3, and then completes the stream.

  2. We subscribed to the numberObservable$ using the subscribe method. The subscription object contains two properties: next and complete. The next function handles the emitted values and the complete function is called when the Observable completes.

  3. When the subscribe method is called, the Observable starts emitting values. The next function logs each emitted value to the console.

  4. After the Observable emits all the values and reaches its completion point (via observer.complete()), the complete function is called, and the subscription is considered completed.

Let's create another simple project using Angular and demonstrate how to use Observables and other core functionalities from RxJS. We will build a basic "User Management" application that fetches user data from a RESTful API and displays it on the screen. Users can be filtered by their roles, and we'll use RxJS Observables to handle the HTTP requests and implement filtering functionality.

Prerequisites: Make sure you have Angular CLI installed. If not, you can install it globally using npm:

npm install -g @angular/cli

Step 1: Create a New Angular Project

Open a terminal and create a new Angular project using the Angular CLI. Generate a new service called userService to manage user data and HTTP requests:

ng new user-management-app
cd user-management-app
ng generate service user

Open the generated user.service.ts file (usually located in the src/app folder) and update it with the following code:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

interface User {
  id: number;
  name: string;
  email: string;
  role: string;
}

@Injectable({
  providedIn: 'root',
})
export class UserService {
  private apiUrl = 'https://jsonplaceholder.typicode.com/users';

  constructor(private http: HttpClient) {}

  getUsers(): Observable<User[]> {
    return this.http.get<User[]>(this.apiUrl);
  }

  getUsersByRole(role: string): Observable<User[]> {
    return this.getUsers().pipe(
      map((users) => users.filter((user) => user.role === role))
    );
  }
}

Step 3: Implement the User Component

Generate a new component called user:

ng generate component user

Update the generated user.component.ts file with the following code:

import { Component, OnInit } from '@angular/core';
import { UserService } from '../user.service';
import { Observable } from 'rxjs';

interface User {
  id: number;
  name: string;
  email: string;
  role: string;
}

@Component({
  selector: 'app-user',
  template: `
    <h2>Users</h2>
    <div *ngFor="let user of users$ | async">
      <p><strong>Name:</strong> {{ user.name }}</p>
      <p><strong>Email:</strong> {{ user.email }}</p>
      <p><strong>Role:</strong> {{ user.role }}</p>
      <hr />
    </div>
    <button (click)="filterByRole('admin')">Show Admins</button>
    <button (click)="filterByRole('user')">Show Users</button>
  `,
})
export class UserComponent implements OnInit {
  users$: Observable<User[]>;

  constructor(private userService: UserService) {}

  ngOnInit() {
    this.users$ = this.userService.getUsers();
  }

  filterByRole(role: string) {
    this.users$ = this.userService.getUsersByRole(role);
  }
}

Step 4: Add the HttpClientModule

Open the app.module.ts file (usually located in the src/app folder) and import the HttpClientModule to enable HTTP requests:

import { HttpClientModule } from '@angular/common/http';

@NgModule({
  imports: [
    HttpClientModule,
  ],
})
export class AppModule {}

Step 5: Update the App Component

Update the app.component.html file (usually located in the src/app folder) to include the app-user component:

<app-user></app-user>

Step 6: Start the Development Server

Run the following command in the terminal to start the development server:

ng serve

Now, visit the localhost URL the file is served on in your web browser, and you should see the "Users" heading along with a list of user details fetched from the API. Additionally, two buttons labeled "Show Admins" and "Show Users" allow you to filter the displayed users by their roles.

Conclusion

So that is a really simple and easy demonstration of what RxJS is and how to use it at the very basic level. We've explored how to use Observables and other core functionalities from RxJS in an Angular application. We utilized Observables to manage HTTP requests and implemented filtering functionality based on user roles. RxJS's operators like map and the async pipe in Angular templates make it straightforward to work with asynchronous data in a reactive and efficient way. You can build on this example to create more complex applications with richer functionality.

RxJS is a fundamental part of Angular development, providing a powerful and expressive way to handle asynchronous operations. By using Observables and a variety of operators, you can efficiently manage HTTP requests, event handling, and state changes in your Angular applications.

Remember to explore the official RxJS documentation for a comprehensive list of operators and their usage in different scenarios. Happy coding with RxJS and Angular!