Angular + switchMap Example
November 17, 2018
This page will walk through Angular and RxJS switchMap
operator Example. RxJS switchMap
emits Observable
after applying the given function to each item emitted by source Observable
. The Observable
emitted by given function that is also called inner Observable
, is returned by switchMap
operator. switchMap
starts emitting items emitted by inner Observable
. When a new inner Observable
is emitted, the switchMap
stops emitting items from previous inner Observable
and starts emitting items from latest inner Observable
. It continues to do in the same way for all subsequent inner Observable
. switchMap
is a RxJS pipeable operator and it is used within pipe
function of Observable
from RxJS 6. switchMap
is imported from rxjs/operators
as following.
import { switchMap } from 'rxjs/operators';
debounceTime
with switchMap
to delay the emitting of values from source Observable
for the given amount of time. The scenarios to use switchMap
are search functionality, accessing URL parameter etc.
Contents
Technologies Used
Find the technologies being used in our example.1. Angular 7.0.0
2. Angular CLI 7.0.3
3. TypeScript 3.1.1
4. Node.js 10.3.0
5. NPM 6.1.0
6. RxJS 6.3.3
7. In-Memory Web API 0.6.1
Using switchMap in Search Operation
Here we will discuss usingswitchMap
in search functionality. In search operation, user may frequently change search keyword. So it is important that we should get result only with latest search keyword. For any keyword switchMap
will emit items from inner Observable
and if before completion a new search keyword is hit, then switchMap
will stop emitting previous inner Observable
and start emitting the latest inner Observable
. Hence user always gets the result for latest search keyword. switchMap
is used as following.
searchBook() { this.bookId.valueChanges.pipe( switchMap(id => { console.log(id); return this.bookService.getBook(id); }) ).subscribe(res => this.book = res); }
switchMap
with debounceTime
:debounceTime
delays the emit of new value from source Observable
for the given time after an emit from source Observable
has taken place. If within the delay due time, more than one values arrived, then debounceTime
keeps track of most recent value and emits the latest one from source Observable
once due time is over. Find the code snippet for switchMap
with debounceTime
.
searchBook() { this.bookId.valueChanges.pipe( debounceTime(500), switchMap(id => { console.log(id); return this.bookService.getBook(id); }) ).subscribe(res => this.book = res); }
debounceTime
is that switchMap
does not get every search keyword which has been entered and changed within the given delay time by the user because debounceTime
emits only the latest value from source Observable
only after the given delay time is over. Hence some useless processing by switchMap
is avoided. In the above example once the value from source Observable
is emitted by debounceTime
then only after 500 milliseconds, the latest value from source Observable
will be emitted to switchMap
operator.
Error handling with
switchMap
:We can use
catchError
with switchMap
to handle any error as following.
searchBook() { this.bookId.valueChanges.pipe( debounceTime(500), switchMap(id => { console.log(id); return this.bookService.getBook(id); }), catchError(err => of(null)) ).subscribe(res => this.book = res); }
Using switchMap to Access URL Parameter in Routing
To access URL query parameter, we useswitchMap
operator in routing. Suppose there are many links and on the click of the link, detail is shown. If the user clicks more than one link in a very short span of time then it is necessary that detail should be shown of only latest clicked link. As we know that switchMap
stops emitting values of inner Observable
if new inner Observable
starts emitting. Find the code snippet to use switchMap
to access parameter in angular routing.
ngOnInit() { this.route.params.pipe( switchMap((params: Params) => this.bookService.getBook(+params['id'])) ) .subscribe(book => this.book = book); }
Complete Example
book.component.tsimport { Component, OnInit } from '@angular/core'; import { Observable, of, pipe } from 'rxjs'; import { switchMap, debounceTime, catchError } from 'rxjs/operators'; import { BookService } from './book.service'; import { Book } from './book'; import { FormControl, FormBuilder, FormGroup } from '@angular/forms'; @Component({ selector: 'app-book', template: ` <h3>Search Book</h3> <form [formGroup]="bookForm"> ID: <input formControlName="bookId"> </form> <br/> <div *ngIf="book"> Id: {{book.id}}, Name: {{book.name}}, Category: {{book.category}} </div> ` }) export class BookComponent implements OnInit { book: Book; constructor(private bookService: BookService, private formBuilder: FormBuilder) { } ngOnInit() { this.searchBook(); } bookId = new FormControl(); bookForm: FormGroup = this.formBuilder.group({ bookId: this.bookId } ); searchBook() { this.bookId.valueChanges.pipe( debounceTime(500), switchMap(id => { console.log(id); return this.bookService.getBook(id); }) ).subscribe(res => this.book = res); } }
import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Params } from '@angular/router'; import { switchMap } from 'rxjs/operators'; import { Book } from './book'; import { BookService } from './book.service'; @Component({ template: ` <div *ngIf="book"> Id: {{book.id}}, Name: {{book.name}}, Category: {{book.category}} </div> ` }) export class FavBookComponent implements OnInit { book: Book; constructor(private bookService: BookService, private route: ActivatedRoute) { } ngOnInit() { this.route.params.pipe( switchMap((params: Params) => this.bookService.getBook(+params['id'])) ) .subscribe(book => this.book = book); } }
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable, of } from 'rxjs'; import { Book } from './book'; import { catchError } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class BookService { bookUrl = "/api/books"; constructor(private http: HttpClient) { } getBook(id: number): Observable<Book> { return this.http.get<Book>(this.bookUrl + "/" + id).pipe( catchError(err => of(null)) ); } }
export interface Book { id: number; name: string; category: string; }
import { Component } from '@angular/core'; @Component({ selector: 'app-root', template: ` <app-book></app-book> <br/><br/> <a [routerLink]="['/my-book', 101]">My Favourite Book</a> <router-outlet></router-outlet> ` }) export class AppComponent { }
import { InMemoryDbService } from 'angular-in-memory-web-api'; export class TestData implements InMemoryDbService { createDb() { let bookDetails = [ { id: 101, name: 'Angular by Krishna', category: 'Angular' }, { id: 102, name: 'Core Java by Vishnu', category: 'Java' }, { id: 103, name: 'NgRx by Rama', category: 'Angular' } ]; return { books: bookDetails }; } }
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { HttpClientModule } from '@angular/common/http'; import { RouterModule } from '@angular/router'; import { AppComponent } from './app.component'; import { BookComponent } from './book.component'; import { FavBookComponent } from './fav-book.component'; //For InMemory testing import { InMemoryWebApiModule } from 'angular-in-memory-web-api'; import { TestData } from './test-data'; @NgModule({ imports: [ BrowserModule, HttpClientModule, FormsModule, ReactiveFormsModule, RouterModule.forRoot([ { path: 'my-book/:id', component: FavBookComponent } ]), InMemoryWebApiModule.forRoot(TestData) ], declarations: [ AppComponent, BookComponent, FavBookComponent ], providers: [ ], bootstrap: [ AppComponent ] }) export class AppModule { }
Run Application
To run the application, find the steps.1. Download source code using download link given below on this page.
2. Use downloaded src in your Angular CLI application. To install Angular CLI, find the link.
3. Install angular-in-memory-web-api@0.6.1
4. Run ng serve using command prompt.
5. Access the URL http://localhost:4200
Find the print screen of the output.

References
RxJS ObservableAngular: The RxJS library
Pipeable Operators