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
}
}
3. Using Async Pipe (Recommended)
@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
Observable | Promise |
---|---|
Lazy (cold) | Eager (hot) |
Cancellable | Not cancellable |
Multiple values | Single value |
Rich operators | Limited methods |
Best Practices
- Always unsubscribe to prevent memory leaks
- Use async pipe when possible
- Handle errors with catchError operator
- Use takeUntil pattern for component cleanup
- 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!