The Angular SwitchMap maps each value from the source observable into an inner observable, subscribes to it, and then starts emitting the values from it. It creates a new inner observable for every value it receives from the Source. Whenever it creates a new inner observable it unsubscribes from all the previously created inner observables. Basically it switches to the newest observable discarding all other.
Table of Contents
Syntax
The syntax of the SwitchMap
operator is as shown below.
1 2 3 | switchMap(project: (value: T, index: number) => O): OperatorFunction<T, ObservedValueOf<O>> |
project:
is a function that we use to manipulate the values emitted by the source observable. The project can accept two arguments. one is value
i.e. the value emitted by the source observable. The second argument is index
number. The index
number starts from 0
for the first value emitted and incremented by one for every subsequent value emitted. It is similar to the index of an array. The project function must return an observable.
SwitchMap Example
To use SwitchMap in Angular first we need to import it our Component or Service.
1 2 3 | import { switchMap } from 'rxjs/operators'; |
The following code shows how to use SwitchMap in Angular. We have two observables srcObservable
which emits 1,2,3,4
& innerObservable
which emits 'A','B','C','D'
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | let srcObservable= of(1,2,3,4) let innerObservable= of('A','B','C','D') srcObservable.pipe( switchMap( val => { console.log('Source value '+val) console.log('starting new observable') return innerObservable }) ) .subscribe(ret=> { console.log('Recd ' + ret); }) //Output Source value 1 starting new observable Recd A Recd B Recd C Recd D Source value 2 starting new observable Recd A Recd B Recd C Recd D Source value 3 starting new observable Recd A Recd B Recd C Recd D Source value 4 starting new observable Recd A Recd B Recd C Recd D |
The project function is the first argument to the switchMap. It takes the values from the srcObservable
. For each value, it receives from the srcObservable
(i. e. 1,2,3 &4) it creates a new observable i.e. innerObservable.
SwitchMap automatically subscribes to the innerObservable returned by the project function. The innerObservable emits the values (A,B,C,D), and pushes it to the stream
Hence the subscribers will receive the values A, B, C, D four times. Once for each value of the srcObservable
.
SwitchMap Vs Map
The map operators emits value as observable. The SwitchMap creates a inner observable, subscribes to it and emits its value as observable.
The Following example shows the difference between them.
The map operator below maps the value coming from the source observable to a new value by multiplying it by 2. It then emits it into the observable stream. The subscribers will receive the values 2,4,6 & 8.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | let obs= of(1,2,3,4) //Using MAP obs.pipe( map(val => { return val*2 //Returning Value }) ) .subscribe(ret=> { console.log('Recd from map : ' + ret); }) //Output Recd from map : 2 Recd from map : 4 Recd from map : 6 Recd from map : 8 |
We can write the above code using SwitchMap as follows. The only thing that changes is how we return the new value in the project function. The map example returns the value as val*2
, while the SwitchMap returns new observable (of(val*2)
) using the of
function. It also subscribes to the newly created observable and emits its value to the stream.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | let obs= of(1,2,3,4) obs.pipe( switchMap( val => { return of(val*2) //Returning observable }) ) .subscribe(ret=> { console.log('Recd from switchMap : ' + ret); }) //Output Recd from switchMap : 2 Recd from switchMap : 4 Recd from switchMap : 6 Recd from switchMap : 8 |
SwitchMap switches to the most recent observable
Whenever SwitchMap subscribes to a new inner observable, it unsubscribes from the previous one.
In the following example, we create an observable from the click
event of a button
using the fromEvent
method. The SwitchMap operator returns an observable using the interval method. The interval method creates an infinite observable, which emits a sequence of integers spaced by a given time interval.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core'; import { of, from, fromEvent, interval, Observable } from 'rxjs'; import { switchMap, map, catchError } from 'rxjs/operators'; @Component({ selector: 'app-root', template: `<button #button>Click Me</button>`, }) export class AppComponent implements AfterViewInit{ @ViewChild('button',{static:true}) button; clicks$:Observable<any>; ngAfterViewInit() { this.clicks$ = fromEvent(this.button.nativeElement, 'click'); this.switchExample(); } switchExample() { this.clicks$ .pipe( switchMap(() => { return interval(500) }) ) .subscribe( val => console.log(val)); } } |
When you click on the button, the clicks
observable emits its first value. The switchMap
replaces it with the interval
observable, which starts emitting value starting from 0 every 500ms.
When you click again, the switchMap
unsubscribes from the previous interval
observable and starts new one, which again starts to emit value from 0.
Using SwitchMap in Angular
The following are some of the real use cases for the SwitchMap in Angular
With Route Parameters
When we use the Angular Router to Pass parameter to route, we need to read it in our component class. We do that by subscribing to the paramMap
to get the id
. We then use the id
to retrieve the product
data.
The code is as shown below.
1 2 3 4 5 6 7 8 | ngOnInit() { this._Activatedroute.paramMap.subscribe(params => { this.service.getProduct(+params.get('id')) .subscribe((product: Product) => this.product = product); }); } |
Consider the example where the user navigates to the /product/1
route. The service will send the query to the database to get the Product with id 1. Now, the user decides to navigate to the route /product/2
. This will also result in another query for the Product being sent to the database. It is possible that the result of the second query arrives before the first query. In such a scenario, we will be in the route /product/2
, while our component displays the data of the product 1.
We can easily solve the above issue using the switchMap
. When SwitchMap
creates the second observable it unsubscribes from all the previous observable. Hence even if the Product 1 data arrives late, it would be discarded as there are no subscribers
1 2 3 4 5 6 7 8 9 10 11 12 | ngOnInit() { this.activatedRoute.paramMap .pipe( switchMap((params: Params) => { return this.service.getProduct(params.get('id')) } )) .subscribe((product: Product) => this.product = product); } |
Angular Forms ValueChanges event
The similar situations described above can also happen when we subscribe to the ValueChanges event and use that to get data from the back end.
1 2 3 4 5 6 7 8 9 10 11 12 | this.mainForm.get("productCode").valueChanges .pipe( debounceTime(700) ) .subscribe(val=> { this.queryDepositData(val) .subscribe(data => { this.product=data; }) }) |
The switchMap ensures that only the result from the last observable populates the Product
1 2 3 4 5 6 7 8 9 10 11 12 | this.mainForm.get("productCode").valueChanges .pipe( debounceTime(700), switchMap(val => { return this.queryDepositData(); }) ) .subscribe(data => { this.product=data; }) |
The example also uses the debounceTime
operator, which emits a value from the source Observable only after a particular time span has passed without another source emission.
References
Read More
It gets so much easier with the real use cases. Much better than official docs.
Excellent
thank you, it is very clear and easy to understand a complex concept
awesome
thanks so much keep going guys
the clearest explanation i ever found about this kind of observable, thank you!
thx very much. this article was very useful for me!
Excellent
Simple and to the point explanation.
thanks
Very use full