concatMap In Angular
In this article, we will learn about the concatMap in Angular for example. The Angular ConcatMap converts each value from the source observable into an inner observable, subscribes to it, and then begins emitting data from it in place of the original. For each value it receives from the Source, it creates a new inner observable. It merges the values of all of its internal observables in the order in which they are subscribed, then emits the values back into the stream. ConcatMap, unlike SwitchMap, does not cancel any of its internal observables. It's similar to MergeMap with the exception that the order of its inner observables is preserved.
Syntax
The concatMap operation has the following syntax.
concatMap(project: (value: T, index: number) => O): OperatorFunction<t observedvalueof="">>
project: We use this function to alter the values emitted by the source observable. There are two arguments to the project function. The first is value, which refers to the value emitted by the source observable. The index number is the second argument. The index number starts at 0 for the first value emitted and increases by one for each item emitted after that. It's comparable to an array's index. An observable must be returned by the project function.
ConcatMap Example
In order to use concatMap in Angular, we must first import it into our Component or Service.
import { concatMap } from 'rxjs/operators';
We have two observables in the following code: srcObservable emits 1,2,3,4 and innerObservable emits 'A','B','C','D'.
let srcObservable= of(1,2,3,4) let innerObservable= of('A','B','C','D') srcObservable.pipe( concatMap( 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 srcObservable provides the values for the ConcatMap. It produces a new observable, called innerObservable, for each value. It also subscribes to the innerObservable automatically. The values (A, B, C, D) are emitted by the innerObservable and pushed to the subscribers.
The way the ConcactMap handles the inner observable differs from the MergeMap. Before establishing a new inner observable, ConcatMap always waits for the preceding one to finish. This ensures that the data is sent to subscribers in the order in which the observables are subscribed.
As a result, the values A, B, C, and D will be delivered four times to subscribers. For each value of the srcObservable, do this once.
ConcatMap Vs Map
Value is emitted as an observable by the map operators. ConcatMap produces an inner observable, subscribes to it, and emits the value of that observable as an observable. It emits the value in the same order as the observable is created.
The distinction between ConcatMap and Map is seen in the following example.
By multiplying the value from the source observable by two, the Map operator below maps it to a new value. The data is subsequently written to the observable stream. The values 2, 4, 6, and 8 will be sent to subscribers.
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
The only difference in the ConcatMap example is how we return the new value from our project function. The map returns the value as val*2, but the concatMap uses the of function to return the value as observable (of(val*2)). It also subscribes to and emits the value of the newly formed observable to the stream.
let obs= of(1,2,3,4) obs.pipe( concatMap( val => { return of(val*2) //Returning observable }) ) .subscribe(ret=> { console.log('Recd from concatMap : ' + ret); }) //Output Recd from concatMap: 2 Recd from concatMap: 4 Recd from concatMap: 6 Recd from concatMap: 8
ConcatMap combines inner observable and keeps the order
None of the inner observables of ConcatMap are ever canceled. It sits and waits for them to complete their tasks and emit value. Before constructing a new observable, it additionally waits for the previous inner observable to end.
Using the fromEvent method, we build an observable from a button's click event in the following example. The ConcatMap operator returns an inner observable delayedObs for each button click.
The delayedObs sends out five values separated by 1000 milliseconds.
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.concatMapExample3(); } 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); }) } concatMapExample3() { let obs= this.clicks$ .pipe( concatMap(() => { this.count=this.count+1; return this.delayedObs(this.count) }) ) .subscribe(val => console.log(val)); } }
The clicks observable emits its first value when you click the button. We increment the count by one inside the concatMap and pass it to the delayedObs. The delayedObs are subscribed to by the concatMap. It begins by emitting values A through E, with the count prefixed.
The concatMap checks if the previous observable has finished when you click again. If this is not the case, it waits for it to complete before subscribing to the delayedObs.
All of the emitted values from all of the concatMap's inner observables are collected and sent to the subscribers.
The following result shows that even if we click the click button numerous times, the results always appear in the correct sequence.
Using ConcatMap in Angular
The following are some real-world examples of how to use the concatMap in Angular.
Merging values from two or more HTTP Calls
Consider the following scenario: you receive data from an observable (outer observable). To access extra data for each of those values, you should call another observable (inner observable). This is an excellent scenario for ConcatMap to be used in.
We have an array of dog breeds in the following example. We make an observable out of the array. This becomes our observable from the outside.
Using the free Dog API, we perform an HTTP request for each of the dog breeds produced by the outer observable to get the sub-breeds. The URL for the HTTP request is built with the dog breed information from the outer observable.
The ConcatMap subscribes to all of its inner observables automatically and waits for them to finish.
of("hound", "mastiff", "retriever") //outer observable .pipe( concatMap(breed => { const url = 'https://dog.ceo/api/breed/' + breed + '/list'; return this.http.get<any>(url) //inner observable }) ) .subscribe(data => { console.log(data) })
Without using the concatMap, the code is as follows. Nested observable is used in the application.
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 ConcatMap
For each value of the outside observable, the ConcatMap creates one inner observable. The ForkJoin Operator can be used to create many inner observables.
In the following example, we send a query for a random image of a dog breed along with a list of breeds. In Angular, we'll need to send two HTTP Get requests to do this. To accomplish so, we build two observables, obs1, and obs2. The forJoin is then used to combine obs1 and obs2 and return a new observable.
MergeHTTPRequestWithFork() { //const url='https://dog.ceo/api/breed/'+hound+'/list'; of("hound", "mastiff", "retriever") .pipe( concatMap(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) }) }
Conclusion
In this article, we learned what is concatMap and how to use it in Angular using examples.
I hope this article helps you and you will like it.👍
If you have any doubt or confusion then free to ask in the comment section.