RXJS switchMap
In this tutorial we shall see what is a switchMap and how to use it.
What is switchMap?
The SwitchMap
operator of rxjs is used to switch between emission. It switches from one observable to the other observable by cancelling the previous observable and switches to the new observable.
Create a new angular project
Lets create an angular application using the angular cli command.
ng new switchmap-tutorial
Create HttpService to handle api calls
Lets create a service named HttpService
to make http api call. Enter the following angular cli command to create a service.
ng g s services/http
Now open the file and modify the code as below.
import { Injectable } from '@angular/core';
import { HttpClient} from '@angular/common/http';
import { Observable} from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class HttpService {
constructor(private http: HttpClient) {
}
getCall(url: string): Observable<any> {
return this.http.get(url);
}
}
Create a service RxjsService to add switchMap
Now create a service using the below angular cli command.
ng g s services/rxjs
This will create us a service in the services folder. Now open the rxjs.service.ts
file and add the following code.
import { Injectable } from '@angular/core';
import { from } from 'rxjs';
import { switchMap} from 'rxjs/operators';
import { HttpService } from '../services/http.service';
@Injectable({
providedIn: 'root'
})
export class RxjsService {
domainUrl = 'https://reqres.in/api/users?page=';
reqArray = [1, 2, 3, 4, 5];
constructor(private httpService: HttpService) { }
switchMapCall() {
from(this.reqArray).pipe(
switchMap(id => this.httpService.getCall(this.domainUrl + id))
).subscribe((value) => {
console.log(value);
});
}
}
The domainUrl
contains the api url.
The reqArray
is an array of integers to make api call by appending to the above domainUrl
.
The switchMapCall()
function, contains the from
rxjs operator, which is used to convert an array to an observable.
Here, the from
operator takes reqArray
array variable as parameter and emit values of array one by one.
Next, we pipe the obervable and pass the array value switchMap
operator one by one.
The switchMap
operator gets the integers in the array one by one in the id
parameter and then it appends the id
to the domainUrl
and form a url and makes an api call.
But before the api call returns, the result the next integer will be piped and passed to the switchMap
, which now cancels the previous api call and forms new url with 2
as id
value and makes api. In the meantime, the value 3
is passed to the id
parameter and the previous api call will be cancelled. Similar thing will happen continously and only the final api call with id
value of 5
will complete the api call and return the result.
The returned result is subscribed using subscribe
function and print the result to the console.
Add RxjsService to the AppComponent
Open the app.component.ts
file and modify the code as below.
import { Component, OnInit } from '@angular/core';
import { RxjsService } from './services/rxjs.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
constructor(private rxjsService: RxjsService) {
}
ngOnInit(): void {
this.rxjsService.switchMapCall();
}
}
Now run your application and press the F12
key to open the debugger option and select the Network
tab and check out.
You will notice only the final api call will be successful and the rest of the calls gets cancelled.
What if suppose you want to call an api and use its result to make another api.
Some of them will come up with the solution of subscribing inside another subscribe function. Thats not a good practise to work with. Lets see how to use switchMap
to solve the problem.
Add syncSwitchMapCall function to make call one after the other using switchMap
Open the rxjs.service.ts
file and add modify the code as below.
import { Injectable } from '@angular/core';
import { from, throwError } from 'rxjs';
import { switchMap, catchError } from 'rxjs/operators';
import { HttpService } from '../services/http.service';
@Injectable({
providedIn: 'root'
})
export class RxjsService {
domainUrl = 'https://reqres.in/api/users?page=';
reqArray = [1, 2, 3, 4, 5];
constructor(private httpService: HttpService) { }
switchMapCall() {
from(this.reqArray).pipe(
switchMap(id => this.httpService.getCall(this.domainUrl + id))
).subscribe((value) => {
console.log(value);
});
}
syncSwitchMapCall() {
this.httpService.getCall(this.domainUrl + '1').pipe(
switchMap((result1) => {
console.log(result1);
// Can process something with the result obtained in result1.
return this.httpService.getCall(this.domainUrl + '2');
}),
switchMap((result2) => {
console.log(result2);
// Can process something with the result obtained in result2.
return this.httpService.getCall(this.domainUrl + '3');
}),
catchError((error) => {
return throwError(error);
})
).subscribe((result3) => {
// Finally, result will be returned only for 3rd api call.
console.log(result3);
}, (error) => {
console.log(`Error occured`, error);
});
}
}
Here, we have created a function syncSwitchMapCall()
.
First, it makes api call to an url
using getCall()
function of HttpService
and pipe
the response and gives it to a switchMap
operator. This operator will get the result and logs it to the console and makes the second call
and returns the observable to the second switchMap operator.
Now it holds the result of the second api call. If we need to get the value from it we can get it from result2
variable. Now the second switchMap
calls the third api call and returns an observable
.
This observable will be subscribed and the final result will be logged to the console.
If any error occurs, it will be catched by the catchError and it throws the error. The error will be caught by the second callback function of subscribe function.
Open the app.component.ts
file and instead of calling switchMapCall
function call the syncSwitchMapCall
function.
import { Component, OnInit } from '@angular/core';
import { RxjsService } from './services/rxjs.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
constructor(private rxjsService: RxjsService) {
}
ngOnInit(): void {
this.rxjsService.syncSwitchMapCall();
}
}
Run your application and press F12
and check the Console
and Network
tab. It looks as below.