Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

RxJS basics for Angular

RxJS is a library for working with streams of events, and it is installed together with Angular. HttpClient, the router and forms return RxJS Observable objects, so you need to know a little of it. Honestly: RxJS is big and not easy, but a typical application needs only the five ideas from this lesson, and component state is easier to keep in signals.

Observable and subscribe

An Observable is a source that can emit one or more values over time — like a Promise that can answer more than once. An HttpClient request emits one value, the server response, and completes.

// src/app/post-list/post-list.ts
this.api.getPosts().subscribe({
  next: (posts) => this.posts.set(posts),
  error: (err) => console.error('Error', err),
  complete: () => console.log('Done'),
});

next receives a value, error receives an error, and complete is called at the end. Most often you only need next, and you pass it as a plain function, as in the previous lesson.

pipe and operators

The pipe() method passes values through operators — functions that transform them. Operators are imported from rxjs:

// src/app/post-api.ts
import { map } from 'rxjs';
 
getTitles() {
  return this.http.get<Post[]>(this.url).pipe(
    map((posts) => posts.slice(0, 5)),
  );
}

The method returns an Observable that emits only the first five posts. A component subscribes to it the same way as before.

OperatorWhat it does
mapTransforms a value
filterLets only matching values through
tapRuns an action without changing the value (a log, for example)
catchErrorCatches an error and substitutes a fallback value
switchMapSwitches to a new Observable, cancelling the previous one

The async pipe

Instead of subscribing by hand, you can give the Observable to the template. The async pipe subscribes and unsubscribes by itself:

// src/app/post-list/post-list.ts
import { Component, inject } from '@angular/core';
import { AsyncPipe } from '@angular/common';
import { PostApi } from '../post-api';
 
@Component({
  selector: 'app-post-list',
  imports: [AsyncPipe],
  templateUrl: './post-list.html',
})
export class PostList {
  posts$ = inject(PostApi).getPosts();
}
<!-- src/app/post-list/post-list.html -->
@if (posts$ | async; as posts) {
  @for (post of posts; track post.id) {
    <h3>{{ post.title }}</h3>
  }
} @else {
  <p>Loading...</p>
}

A dollar sign at the end of the name is the usual marker for an Observable. Until the response arrives, async returns null, and "Loading..." is shown.

toSignal

The toSignal() function turns an Observable into a signal — so HTTP data is read in the template like any signal:

// src/app/post-list/post-list.ts
import { Component, inject } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { PostApi } from '../post-api';
 
@Component({
  selector: 'app-post-list',
  templateUrl: './post-list.html',
})
export class PostList {
  posts = toSignal(inject(PostApi).getPosts(), { initialValue: [] });
}

The template uses posts(), as in the data loading lesson. Subscribing and unsubscribing happen automatically.

Common mistake

Subscribing by hand to a long-lived stream, such as route parameter changes, and never unsubscribing. The subscription outlives the component and keeps running. Use async, toSignal or the takeUntilDestroyed() operator from @angular/core/rxjs-interop — they unsubscribe by themselves.

Thanks for reading!

Donate to the site — we work hard for you.