The Angular MergeMap maps each value from the source observable into an inner observable, subscribes to it, and then starts emitting the values from it replacing the original value. It creates a new inner observable for every value it receives from the Source. Unlike SwitchMap, MergeMap does not cancel any of its inner observables. It merges the values from all of its inner observables and emits the values back into the stream.
Table of Contents
Syntax
The syntax of the MergeMap
operator is as shown below.
1 2 3 | mergeMap(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 function accepts 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.
MergeMap Example
To use MergeMap in Angular first we need to import it our Component or Service.
1 2 3 | import { mergeMap } from 'rxjs/operators'; |
The following code shows how to use MergeMap 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( mergeMap( 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 MergeMap. 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.
MergeMap 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
.
MergeMap Vs Map
The map operators emit value as observable. The MergeMap creates an inner observable, subscribes to it, and emits its value as observable.
The Following example shows the difference between MergeMap & Map.
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 achieve the same using the MergeMap also. The only thing that changes is how we return the new value from our project function. The map returns the value as val*2
, while the MergeMap returns the value as 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( mergeMap( val => { return of(val*2) //Returning observable }) ) .subscribe(ret=> { console.log('Recd from mergeMap : ' + ret); }) //Output Recd from mergeMap: 2 Recd from mergeMap: 4 Recd from mergeMap: 6 Recd from mergeMap: 8 |
MergeMap combines results of inner observable
MergeMap never cancels any of its inner observable. It waits for them to finish and emit value. Note that the inner observable’s might finish in an order that is different from the order in which they are subscribed. MergeMap does not care about the order.
In the following example, we create an observable from the click
event of a button
using the fromEvent
method. On every click of the button, the MergeMap operator returns an inner observable delayedObs
The delayedObs
emits 5 values separated by 1000 ms.
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 43 44 45 | 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.mergeMapExample(); } delayedObs(count:number) { return new Observable((observer) => { setTimeout(() => { observer.next(count+" A") }, 1000); setTimeout(() => { observer.next(count+" B") }, 2000); setTimeout(() => { observer.next(count+" C") }, 3000); setTimeout(() => { observer.next(count+" D") }, 4000); setTimeout(() => { observer.next(count+" E"); observer.complete() }, 5000); }) } mergeMapExample() { let obs= this.clicks$ .pipe( mergeMap(() => { this.count=this.count+1; return this.delayedObs(this.count) }) ) .subscribe(val => console.log(val)); } } |
When you click on the button, the clicks
observable emits its first value. Inside the MergeMap
we increase the count by 1 and pass it to the delayedObs
. The mergeMap
subscribes to the delayedObs
. It starts emitting values A to E prepended by the count.
When you click again, the mergeMap
again subscribes to the delayedObs
and it starts to emit the values again.
The mergeMap
collects all the emitted values as they arrive from all of its inner observables and emits it into the subscribers.
Using MergeMap in Angular
The following are some of the real use cases for the MergeMap in Angular
Merging values from two or more HTTP Calls
Consider a scenario where you receive data from an observable (outer observable). For each of those values, you want to call another observable (inner observable) to get more data. The scenario like this is an ideal use case for MergeMap
In the following example, we have an array of a breed of dogs. We convert the array into an observable. This becomes our outer observable
For each of those dog breeds emitted by the outer observable, we make an HTTP request to get the sub-breeds using the free Dog API. The URL for the HTTP request constructed using the dog breed, which we receive from the outer observable.
The MergeMap
automatically subscribes to all of its inner observable and waits for them to complete. It then pushes the values from them into the subscribers.
1 2 3 4 5 6 7 8 9 10 11 12 | of("hound", "mastiff", "retriever") //outer observable .pipe( mergeMap(breed => { const url = 'https://dog.ceo/api/breed/' + breed + '/list'; return this.http.get<any>(url) //inner observable }) ) .subscribe(data => { console.log(data) }) |
The above code, without using the MergeMap
is as follows. The code makes use of nested observable.
1 2 3 4 5 6 7 8 9 10 11 | of("hound", "mastiff", "retriever") .subscribe(breed => { const url = 'https://dog.ceo/api/breed/' + breed + '/list'; this.http.get<any>(url) .subscribe(data => { console.log(data) }) }) |
Using ForkJoin with MergeMap
The MergeMap create a one inner observable for each value of outer observable. To Create more than one inner observable, we can make use of the ForkJoin
Operator.
In the following example, along with a list of breeds, we also send a query for a random image of the dog breed. This requires us to send two HTTP get request in Angular. We create two observables obs1
& obs2
to do that. Then we use the forJoin
to merge obs1
with obs2
and return a new observable.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | MergeHTTPRequestWithFork() { //const url='https://dog.ceo/api/breed/'+hound+'/list'; of("hound", "mastiff", "retriever") .pipe( mergeMap(breed => { const url1 = 'https://dog.ceo/api/breed/' + breed + '/list'; const url2 = 'https://dog.ceo/api/breed/' + breed + '/images/random'; let obs1= this.http.get<any>(url1) let obs2= this.http.get<any>(url2) return forkJoin(obs1,obs2) }) ) .subscribe(data => { console.log(data) }) } |
References
Read More
Can you publish an article about combinelatest please
What is difference between mergeMap and forkJoin?
forkJoin is used to fetch data from multiple api at a same time .
mergeMap is used to manipulate in original value and return a observable.
how to store each data from the subscribe ?
On success of subscribe store those data to your required storage mode
Observable.subscribe(
next : (value) => {
console.log(“Success : “,value);
//here you can write your logic to store each data to your storage mode.
},
error : (err)=>{
console.log(“error : “,err);
},
complete : () =>{
console.log(“completed”);
}
)