Angular course · Module 7: RxJS

Subscription Management

2 min read ·
In this lesson4

Memory leaks from unfinished subscriptions are a serious problem - like a ninja who forgot to return from a mission!

import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { DestroyRef, inject } from '@angular/core';

@Component({...})
export class ModernComponent {
  private destroyRef = inject(DestroyRef);

  constructor() {
    // Automatically completes when the component is destroyed
    this.dataService.getData().pipe(
      takeUntilDestroyed()
    ).subscribe(data => {
      this.processData(data);
    });

    // Can also be used outside the constructor
    this.loadData();
  }

  loadData(): void {
    this.http.get('/api/data').pipe(
      takeUntilDestroyed(this.destroyRef)
    ).subscribe();
  }
}

Traditional Approach with Subject

import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

@Component({...})
export class TraditionalComponent implements OnDestroy {
  private destroy$ = new Subject<void>();

  ngOnInit(): void {
    this.dataService.getData().pipe(
      takeUntil(this.destroy$)
    ).subscribe(data => {
      this.processData(data);
    });

    // Multiple subscriptions
    this.otherService.getOther().pipe(
      takeUntil(this.destroy$)
    ).subscribe();
  }

  ngOnDestroy(): void {
    this.destroy$.next();
    this.destroy$.complete();
  }
}

AsyncPipe - Automatic Management

@Component({
  template: `
    @if (samuraiList$ | async; as samuraiList) {
      @for (s of samuraiList; track s.id) {
        <div>{{ s.name }}</div>
      }
    } @else {
      <p>Loading...</p>
    }
  `
})
export class SamuraiListComponent {
  // AsyncPipe automatically subscribes and unsubscribes!
  samuraiList$ = this.http.get<Samurai[]>('/api/samurai');
}

Subscription Collection

import { Subscription } from 'rxjs';

@Component({...})
export class CollectionComponent implements OnDestroy {
  private subscriptions = new Subscription();

  ngOnInit(): void {
    // Add subscriptions to the collection
    this.subscriptions.add(
      this.service1.data$.subscribe()
    );

    this.subscriptions.add(
      this.service2.data$.subscribe()
    );
  }

  ngOnDestroy(): void {
    // One line cancels all
    this.subscriptions.unsubscribe();
  }
}

Docs entries used in this lesson

Spotted a mistake in this lesson?

Check yourself

Answer the questions from this lesson. Pick an answer to see right away whether it is correct.

  1. 1. What is the recommended way to manage subscriptions in Angular 16+?

  2. 2. What does AsyncPipe do automatically?

These are 2 of 3 questions for this lesson. Solve the rest in the game.

Hands-on tasks in the game

  • Horizontal ordering

    Arrange the import of the takeUntilDestroyed operator from the interop package:

  • Code editor

    Complete the code so that the interval() subscription ends by itself when the component is destroyed.

  • Vertical ordering

    Arrange the steps of the traditional subscription cleanup pattern with Subject in the order they appear in a component:

  • Click in order

    Arrange the AsyncPipe expression with an alias in a template: samurai$ | async; as samurai

Useful articles