Angular Observables And Rxjs Basics Complete Guide
Understanding the Core Concepts of Angular Observables and RxJS Basics
Angular Observables and RxJS Basics: A Comprehensive Guide
Angular applications can handle asynchronous operations effectively using Reactive Extensions for JavaScript (RxJS). At the core of this technique are Observables, which provide a powerful way to work with data streams. This approach is particularly useful for handling events, HTTP requests, and other asynchronous activities in modern web applications.
What are Observables?
An Observable is a data stream provider that emits values over time. It's similar to promises in JavaScript but can emit multiple values and provide additional functionalities such as chaining methods for transformation and filtering. Observables can be used for a variety of purposes, including managing UI state changes, processing user inputs, and consuming APIs.
Creating Observables
To make use of observables, you need to create them first. You can create an observable from various sources using the Observable
constructor. Here’s how:
import { Observable } from 'rxjs';
const numbers$ = new Observable<number>(subscriber => {
subscriber.next(1);
subscriber.next(2);
subscriber.next(3);
setTimeout(() => subscriber.next(4), 1000);
});
numbers$.subscribe(x => console.log(x)); // prints: 1 2 3 4 after 1 second
This example creates an observable, numbers$
, that emits four numeric values: 1
, 2
, and 3
immediately, and 4
after a delay of one second. The $
symbol is a common convention among developers to denote variables that store observables.
Subscribing to Observables
Once you have created an observable, you need to subscribe to it to process emitted values. Subscribing to an observable triggers execution and allows you to define callback functions for handling emitted data and errors.
numbers$.subscribe({
next(num) { console.log('Received number:', num); },
error(err) { console.error('Error occurred:', err); },
complete() { console.log('Completed sequence'); }
});
In this example, next()
processes data received from the observable, error()
handles any errors that occur during execution, and complete()
signals when no more data will be emitted.
Operators
RxJS comes packed with powerful operators that can manipulate and transform observables' emitted data. Some commonly used operators include:
map: Transforms data emitted by an observable.
numbers$.pipe( map(num => num * 2) ).subscribe(num => console.log(num)); // prints: 2 4 6 8
filter: Filters emitted data based on a condition.
numbers$.pipe( filter(num => num > 2) ).subscribe(num => console.log(num)); // prints: 3 4
concat: Combines two or more observables sequentially.
import { concat } from 'rxjs'; concat(numbers$,Observable.of(5,6)).subscribe(num => console.log(num)); // prints: 1 2 3 4 5 6
merge: Combines observables concurrently, emitting their values as they arrive.
import { merge } from 'rxjs'; merge(numbers$, Observable.of(5, 6)).subscribe(num => console.log(num)); // might print: 1 5 2 6 3 4 or 5 1 6 2 3 4 depending on timing
debounceTime: Ignores source values for a specified duration whenever a new value arrives.
const input$ = new Observable(e => e.next('input triggered')); input$.pipe( debounceTime(1000) ).subscribe(event => console.log(event)); // logs once every second
catchError: Recovers from an observable error by providing a fallback observable.
import { throwError } from 'rxjs'; import { catchError } from 'rxjs/operators'; const observableWithError$ = throwError('Something went wrong'); observableWithError$.pipe( catchError(() => { return Observable.of('Fallback Value'); }) ).subscribe({ next(val) { console.log(val); }, error(err) { console.error(err); }}); // prints: Fallback Value
Unsubscribing from Observables
It's crucial to unsubscribe from observables when they're no longer needed, especially when dealing with long-lived subscriptions like those tied to user interface events. Angular automatically unsubscribes from most observables in components once they're destroyed, but you may need to manually unsubscribe in other scenarios.
const subscription = numbers$.subscribe(x => console.log(x));
// unsubscribe when data processing is complete
subscription.unsubscribe();
Subjects
Subjects are a type of observable that can multicast a single execution to many subscribers. They can act as both observables and observers, accepting emissions via next()
, error()
, and complete()
methods and emitting these to subscribers.
There are several kinds of subjects including:
BehaviorSubject: Stores the current value and emits it to all new subscribers. It requires an initial value.
import { BehaviorSubject } from 'rxjs'; const behaviorSubject = new BehaviorSubject(10); behaviorSubject.subscribe({ next(val) { console.log('New behaviorSubject subscription', val); }}); // prints: New behaviorSubject subscription 10 behaviorSubject.next(20); // prints: New behaviorSubject subscription 20
ReplaySubject: Emits recent values to subscribers without a starting point.
import { ReplaySubject } from 'rxjs'; const replaySubject = new ReplaySubject(2); replaySubject.next(1); replaySubject.next(2); replaySubject.subscribe({ next(val) { console.log('ReplaySubject subscription:', val); }}); // prints: ReplaySubject subscription: 1 // prints: ReplaySubject subscription: 2
AsyncSubject: Emits only the last value before completion to each subscriber.
import { AsyncSubject } from 'rxjs'; const asyncSubject = new AsyncSubject(); asyncSubject.next(1); asyncSubject.next(2); asyncSubject.complete(); asyncSubject.subscribe({ next(val) { console.log('AsyncSubject subscription:', val); }}); // prints: AsyncSubject subscription: 2
Use Cases
Here are some practical examples where observables come in handy:
HTTP Requests: To manage and track the status of API calls, observables simplify handling responses asynchronously.
import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; constructor(private http: HttpClient) {} fetchData(): Observable<any> { return this.http.get('https://api.example.com/data'); }
Component Communication: To share information between components using services, observables provide a decoupled and robust solution.
// Service file import { Injectable } from '@angular/core'; import { Subject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class ShareService { private subject = new Subject<any>(); sendMessage(message: any) { this.subject.next({ text: message }); } clearMessage() { this.subject.next(null); } getMessage(): Observable<any> { return this.subject.asObservable(); } } // Subscribing Component import { Component, OnInit } from '@angular/core'; import { ShareService } from './share.service'; @Component({ selector: '...', template: '...' }) export class ComponentB implements OnInit { constructor(private shareService: ShareService) {} ngOnInit() { this.shareService.getMessage().subscribe(message => { console.log(message); }); this.shareService.sendMessage('Hello From Component B!'); } }
Summary
Angular Observables and RxJS form the backbone of reactive programming in Angular applications. Using observables allows developers to manage complex asynchronous flows easily, while RxJS provides a suite of tools for handling and transforming data streams effectively. Understanding this concept is vital for building efficient, scalable, and responsive Angular apps. By leveraging observables and their operators, developers can create robust mechanisms for event handling, data binding, and asynchronous operations, leading to improved app performance and better user experience.
This topic encompasses essential concepts like creating observables, subscribing to them, understanding operators, working with subjects, and best practices around managing subscriptions. Each element plays a pivotal role in mastering reactive programming within the Angular framework.
Online Code run
Step-by-Step Guide: How to Implement Angular Observables and RxJS Basics
Step 1: Introduction to Observables and RxJS
Observables are a core concept of RxJS, a library for working with streams of data. An observable is an object that represents a stream of data and can emit events over time.
Step 2: Setting Up Angular Project
First, create a new Angular project:
ng new observable-demo
cd observable-demo
Step 3: Importing RxJS in Angular
RxJS
is already included in new Angular projects, so you don't need to install it separately. You can import necessary operators and functions from RxJS
.
Step 4: Simple Observable Example
Create a new component:
ng generate component simple-observable
Open
simple-observable.component.ts
:import { Component, OnInit } from '@angular/core'; import { Observable, Observer } from 'rxjs'; @Component({ selector: 'app-simple-observable', template: `<p>Simple Observable Example</p>` }) export class SimpleObservableComponent implements OnInit { ngOnInit(): void { // Create an observable instance const simpleObservable = new Observable((observer: Observer<number>) => { console.log('Simple Observable started'); observer.next(1); observer.next(2); observer.next(3); observer.complete(); observer.next(4); // This will not be emitted as the observable has already completed }); // Subscribe to the observable simpleObservable.subscribe({ next: (value) => console.log('Received value: ' + value), error: (err) => console.log('Something went wrong: ' + err), complete: () => console.log('Observable completed') }); } }
Update
app.component.html
:<app-simple-observable></app-simple-observable>
Run the application:
ng serve
Open your browser and go to
http://localhost:4200
. Check the browser console for the output.Simple Observable started Received value: 1 Received value: 2 Received value: 3 Observable completed
Step 5: Using Operators
Operators are methods that can be called upon an observable to transform the values emitted by the source observable.
Create another component for operators:
ng generate component observable-operators
Open
observable-operators.component.ts
:import { Component, OnInit } from '@angular/core'; import { Observable, from } from 'rxjs'; import { map, filter, tap } from 'rxjs/operators'; @Component({ selector: 'app-observable-operators', template: `<p>Observable Operators Example</p>` }) export class ObservableOperatorsComponent implements OnInit { ngOnInit(): void { const numbers = from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); numbers.pipe( filter(num => num % 2 === 0), // Filter even numbers map(num => num * 2), // Multiply by 2 tap(num => console.log(`Doubled even number: ${num}`)) // Log intermediate values ).subscribe(result => console.log('Result:', result)); } }
Update
app.component.html
:<app-simple-observable></app-simple-observable> <app-observable-operators></app-observable-operators>
Run the application again:
ng serve
Open your browser and check the console for the output:
Login to post a comment.