CPCODELAB
Angular

RxJS and Observables

14 min read

Reactive Programming with RxJS

RxJS (Reactive Extensions for JavaScript) is a library for composing asynchronous and event-based programs using observable sequences. Angular uses RxJS throughout — HttpClient, Router.events, FormControl.valueChanges, and many lifecycle APIs all return Observables.

An Observable represents a stream of values over time. You .subscribe() to start receiving values. Operators like map, filter, switchMap, debounceTime, and combineLatest let you transform and combine streams without deeply nested callbacks.

ts
import { Component, OnInit, inject } from '@angular/core';
import { AsyncPipe } from '@angular/common';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { HttpClient } from '@angular/common/http';
import {
  Observable, debounceTime, distinctUntilChanged,
  switchMap, catchError, of
} from 'rxjs';

interface SearchResult {
  id: number;
  name: string;
}

@Component({
  selector: 'app-live-search',
  standalone: true,
  imports: [ReactiveFormsModule, AsyncPipe],
  template: `
    <input [formControl]="query" placeholder="Search..." />
    <ul>
      @for (result of results$ | async; track result.id) {
        <li>{{ result.name }}</li>
      }
    </ul>
  `
})
export class LiveSearchComponent implements OnInit {
  private http = inject(HttpClient);
  query = new FormControl('');
  results$!: Observable<SearchResult[]>;

  ngOnInit() {
    this.results$ = this.query.valueChanges.pipe(
      debounceTime(300),
      distinctUntilChanged(),
      switchMap(term =>
        term
          ? this.http.get<SearchResult[]>('/api/search?q=' + term)
          : of([])
      ),
      catchError(() => of([]))
    );
  }
}

The async Pipe

The async pipe subscribes to an Observable (or Promise) in the template and automatically unsubscribes when the component is destroyed. It is the preferred way to consume Observables in templates — no manual subscribe / unsubscribe needed.

Use switchMap when you want to cancel a previous inner Observable on each new emission (ideal for search). Use mergeMap when you want concurrent inner Observables. Use concatMap to queue them sequentially.

Ready to test yourself?
Practice Angular— quiz & coding exercises