In this guide, we’ll be learning about the switchMap operator. The switchMap operator maps each value to an observable, then it flattens all of the inner observables. It basically projects each source value to an observable which is then merged in the output observable, emitting values only from the most recently projected observable.
switchMap<T, R, O extends ObservableInput
The parameter project in the syntax is a function that, when applied to an item emitted by the source observable, returns an observable. The parameter resultSelector is optional and the default value of this parameter is undefined.
The return value, “OperatorFunction<T, ObservedValueOf
The switchMap operator returns an observable that emits items based on applying a function that you supply to each item emitted by the source observable where that function returns an inner observable. Each time it observes one of these inner observables, the output observable begins emitting the items emitted by that inner observable.
When a new inner observable is emitted, switchMap stops emitting items from the previously-emitted inner observable and begins emitting items from the new one. It continues to behave like this for subsequent inner observables.
1import { of } from 'rxjs';
2import { switchMap } from 'rxjs/operators';
3
4const switched = of(1, 2, 3).pipe(switchMap((x: number) => of(x, x * 2, x * 3)));
5switched.subscribe(x => console.log(x));
6// outputs
7// 1
8// 1
9// 1
10// 2
11// 4
12// 6
13// ... and so, on
As we can see in the above example, by using the switchMap operator, we are logging the values by switching to the recent observable using the switchMap operator and emitting the value from that observer. Let’s see another example where we are returning an interval observable on every click event. After every click, we are logging value after a time interval.
1import { fromEvent, interval } from 'rxjs';
2import { switchMap } from 'rxjs/operators';
3
4const clicks = fromEvent(document, 'click');
5const mapResult = clicks.pipe(switchMap((ev) => interval(1000)));
6mapResult.subscribe(x => console.log(x));
Let’s now look at how we can use the switchMap operator in Angular routing. We can use the switchMap operator with Activated Route.
First, import the Router, ActivatedRoute, and ParamMap tokens from the router package. Then, import the switchMap operator because you will need it later to process the observable route parameters. As usual, you’ll write a constructor that asks Angular to inject services that the component requires and reference them as private variables.
Later, in the ngOnInit method, you’ll use the ActivatedRoute service to retrieve the parameters for the route, pull the employee ID from the parameters, and retrieve the employee to display. The paramMap processing is a bit tricky. When the map changes, you’ll get() the ID parameter from the changed parameters.
You might think now is the time to use the RxJS map operator but the EmployeeService operator returns an Observable
Filename: employe-detail.component.ts
1import { switchMap } from 'rxjs/operators';
2import { Component, OnInit } from '@angular/core';
3import { Router, ActivatedRoute, ParamMap } from '@angular/router';
4import { Observable } from 'rxjs';
5
6import { EmployeeService } from '../employee.service';
7import { Employee} from '../employee;
8
9@Component({
10 selector: 'app-employee-detail',
11 templateUrl: './employee-detail.component.html',
12 styleUrls: ['./employee-detail.component.css']
13})
14export class EmployeeDetailComponent implements OnInit {
15 employee$: Observable<Hero>;
16
17 constructor(
18 private route: ActivatedRoute,
19 private router: Router,
20 private service: EmployeeService
21 ) {}
22
23 ngOnInit() {
24 this.employee$ = this.route.paramMap.pipe(
25 switchMap((params: ParamMap) =>
26 this.service.getEmployee(params.get('id')))
27 );
28 }
29 }
30}
Let’s look at another example of the search functionality of employees. By exploring this example, we will be able to understand the characteristics of the switchMap() operator.
Filename: search.component.html
1<input (keyup)="search($event.target.value)" id="name" placeholder="Search"/>
2
3<ul>
4 <li *ngFor="let employee of employee$ | async">
5 <b>{{employee.name}} v.{{employee.address}}</b> -
6 <i>{{employee.description}}</i>
7 </li>
8</ul>
The (keyup) event binding sends every keystroke to the component's search() method. Sending a request for every keystroke could be expensive. It's better to wait until the user stops typing and then send a request. That's easy to implement with RxJS operators, as shown in this excerpt.
Filename: search.component.ts
1withRefresh = false;
2employees$: Observable<NpmEmployeeInfo[]>;
3private searchText$ = new Subject<string>();
4
5search(employeeName: string) {
6 this.searchText$.next(employeeName);
7}
8
9ngOnInit() {
10 this.employees$ = this.searchText$.pipe(
11 debounceTime(500),
12 distinctUntilChanged(),
13 switchMap(employeeName =>
14 this.searchService.search(employeeName, this.withRefresh))
15 );
16}
17
18constructor(private searchService: EmployeeSearchService) { }
The searchText$ is the sequence of search-box values coming from the user. It's defined as an RxJS Subject, which means it is a multicasting observable that can also produce values for itself by calling next(value), as happens in the search() method. Rather than forward every searchText value directly to the injected EmployeeSearchService, the code in ngOnInit() pipes search values through three operators:
switchMap() - send the search request to the service.
The switchMap() operator has three important characteristics.
It returns service responses in their original request order, even if the server returns them out of order.
In this guide, we have explored how we can use the switchMap operator to transform data in Angular. We have also seen how we can use the switchMap operator along with other operators and pipe functions in our application. You can learn more about subscribing to and unsubscribing from observables in Angular in my guide Subscribing to and Unsubscribing from Observables.