Skip to main content

Observables in Angular

Observables are a fundamental concept in Angular, based on the RxJS library. They provide a powerful way to handle asynchronous data streams and events.

What is an Observable?

An Observable is a lazy collection of multiple values over time. Think of it as a stream of data that you can subscribe to and react to changes.

import { Observable } from 'rxjs';

// Creating a simple observable
const observable = new Observable(observer => {
observer.next('Hello');
observer.next('World');
observer.complete();
});

Key Characteristics

  • Lazy: Observables don't execute until you subscribe to them
  • Cancellable: You can unsubscribe to stop receiving data
  • Composable: You can chain operators to transform data
  • Multi-value: Can emit multiple values over time

Common Use Cases in Angular

1. HTTP Requests

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';

@Injectable()
export class DataService {
constructor(private http: HttpClient) {}

getData() {
return this.http.get<any[]>('api/data'); // Returns Observable
}
}

2. Component Usage

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';

@Component({
selector: 'app-data',
template: `<div *ngFor="let item of data">{{item.name}}</div>`
})
export class DataComponent implements OnInit, OnDestroy {
data: any[] = [];
private subscription: Subscription;

constructor(private dataService: DataService) {}

ngOnInit() {
this.subscription = this.dataService.getData().subscribe(
data => this.data = data,
error => console.error(error)
);
}

ngOnDestroy() {
this.subscription.unsubscribe(); // Prevent memory leaks
}
}
@Component({
selector: 'app-data',
template: `<div *ngFor="let item of data$ | async">{{item.name}}</div>`
})
export class DataComponent {
data$ = this.dataService.getData(); // Observable

constructor(private dataService: DataService) {}
}

Common RxJS Operators

Map - Transform data

this.dataService.getData().pipe(
map(data => data.filter(item => item.active))
).subscribe(filteredData => console.log(filteredData));

CatchError - Handle errors

import { catchError } from 'rxjs/operators';
import { of } from 'rxjs';

this.dataService.getData().pipe(
catchError(error => {
console.error(error);
return of([]); // Return empty array on error
})
).subscribe(data => this.data = data);

Debounce - Delay emissions

import { debounceTime } from 'rxjs/operators';

searchControl.valueChanges.pipe(
debounceTime(300) // Wait 300ms after user stops typing
).subscribe(searchTerm => this.search(searchTerm));

Observable vs Promise

ObservablePromise
Lazy (cold)Eager (hot)
CancellableNot cancellable
Multiple valuesSingle value
Rich operatorsLimited methods

Best Practices

  1. Always unsubscribe to prevent memory leaks
  2. Use async pipe when possible
  3. Handle errors with catchError operator
  4. Use takeUntil pattern for component cleanup
  5. Avoid nested subscriptions
// Good: Using takeUntil pattern
private destroy$ = new Subject<void>();

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

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

Observables are essential for building reactive Angular applications and handling asynchronous operations effectively!