�RxJS Higher-order Mapping Operators
(Links to slides and code provided)�
Deborah Kurata
Developer | Pluralsight Author | Consultant | MVP | GDE
deborahkurata
What is a higher-order mapping operator?
When would you use one?
What the heck is an “inner Observable”?
What’s the difference between a map, switchMap, mergeMap and concatMap?
Deborah Kurata
Developer
Pluralsight Author
Angular Getting Started
Angular Reactive Forms
Angular Routing
RxJS in Angular: Reactive Development
Angular NgRx: Getting Started
C# OOP & Best Practices
Microsoft Most Valuable Professional (MVP)
Google Developer Expert (GDE)
@deborahkurata
�RxJS First Order vs Higher-order �Mapping Operators�Part 1: First Order Mapping
Deborah Kurata
Developer | Pluralsight Author | Consultant | MVP | GDE
deborahkurata
�RxJS First Order vs Higher-order �Mapping Operators�Part 2: Higher-order Mapping
Deborah Kurata
Developer | Pluralsight Author | Consultant | MVP | GDE
deborahkurata
First Order vs Higher Order Mapping
First order mapping�transforms each emitted value and emits the result
Higher order mapping�transforms each emitted value to an Observable
map(x => x * 2);
switchMap(id =>
this.http.get(`${url}/${id}`)
)
@deborahkurata
Mapping
(First Order)
@deborahkurata
@deborahkurata
{ data: [
{
id: 1,
productName: 'Palantir'
},
{
id: 2,
productName: 'Rope'
},
{
id: 5,
productName: 'Sword'
}
]
}
[
{
id: 1,
productName: 'Palantir'
},
{
id: 2,
productName: 'Rope'
},
{
id: 5,
productName: 'Sword'
}
]
Map ->
products$ = this.http.get<ProductData>(this.productsUrl)
.pipe(
map(response => response.data),
catchError(this.handleError)
);
@deborahkurata
Map ->
product$ = this.http.get<Product>(this.url)
.pipe(
map(p => ({ ...p,
profit: p.price - p.cost}) as Product),
catchError(this.handleError)
);
{
id: 5,
productName: 'Sword',
productCode: 'WEA-0048',
releaseDate: 'May 21, 2018',
description: 'Flame of the West',
price: 849.9,
cost: 520.0,
starRating: 4.9
}
{
id: 5,
productName: 'Sword',
productCode: 'WEA-0048',
releaseDate: 'May 21, 2018',
description: 'Flame of the West',
price: 849.9,
cost: 520.0,
profit: 329.9,
starRating: 4.9
}
@deborahkurata
Map ->
pageTitle$ = this.product$
.pipe(
map((p: Product) =>
p ? `Product Detail for: ${p.productName}` : null)
);
"Product Detail for: Sword"
{
id: 5,
productName: 'Sword',
productCode: 'WEA-0048',
releaseDate: 'May 21, 2018',
description: 'Flame of the West',
price: 849.9,
cost: 520.0,
profit: 329.9,
starRating: 4.9
}
@deborahkurata
@deborahkurata
Map ->
product$ = this.productSelectedAction$
.pipe(
map(selectedId =>
this.http.get<Product>(`${url}/${selectedId}`)
.pipe(
tap(response => console.log(response))
)
));
selectedId = 5
{
id: 5,
productName: 'Sword',
productCode: 'WEA-0048',
releaseDate: 'May 21, 2018',
description: 'Flame of the West',
price: 849.9,
cost: 520.0,
profit: 329.9,
starRating: 4.9
}
Doesn't work!!
@deborahkurata
product$ = this.productSelectedAction$
.pipe(
map(selectedId =>
this.http.get<Product>(`${url}/${selectedId}`)
.pipe(
tap(response => console.log(response))
)
));
Source or �Outer Observable
Inner Observable
@deborahkurata
product$ = this.productSelectedAction$
.pipe(
map(selectedId =>
this.http.get<Product>(`${url}/${selectedId}`)
.pipe(
tap(response => console.log(response))
)
));
Observable<Observable<Product>>
No subscription
@deborahkurata
Higher-order Mapping Operators
@deborahkurata
Higher-order Mapping Operators
Higher-order Mapping Operator
product$ = this.productSelectedAction$
.pipe(
switchMap(selectedId =>
this.http.get<Product>(`${url}/${selectedId}`)
.pipe(
tap(response => console.log(response))
)
));
Observable<Product>
Automatically subscribes and unsubscribes from inner Observable
@deborahkurata
Higher-order Mapping Operators
mergeMap (flatMap)
concatMap
switchMap
mergeMap
aka flatMap
@deborahkurata
mergeMap
product$ = this.productSelectedAction$
.pipe(
mergeMap(selectedId =>
this.http.get<Product>(`${url}/${selectedId}`)
.pipe(
tap(response => console.log(response))
)
));
On subscribe to the source stream:
Takes in an
Observable stream
Subscribes to that stream
Creates an
output stream
@deborahkurata
mergeMap
product$ = this.productSelectedAction$
.pipe(
mergeMap(selectedId =>
this.http.get<Product>(`${url}/${selectedId}`)
.pipe(
tap(response => console.log(response))
)
));
Item is mapped to the inner Observable
Subscribes to that inner Observable
Inner Observable emissions concatenated to the output stream
When an item is emitted from the source stream:
Unsubscribes from that inner Observable
@deborahkurata
Use mergeMap
concatMap
@deborahkurata
concatMap
product$ = this.productSelectedAction$
.pipe(
concatMap(selectedId =>
this.http.get<Product>(`${url}/${selectedId}`)
.pipe(
tap(response => console.log(response))
)
));
On subscribe to the source stream:
Takes in an
Observable stream
Subscribes to that stream
Creates an
output stream
@deborahkurata
concatMap
product$ = this.productSelectedAction$
.pipe(
concatMap(selectedId =>
this.http.get<Product>(`${url}/${selectedId}`)
.pipe(
tap(response => console.log(response))
)
));
When an item is emitted from the source stream:
Emitted item is queued
@deborahkurata
concatMap
product$ = this.productSelectedAction$
.pipe(
concatMap(selectedId =>
this.http.get<Product>(`${url}/${selectedId}`)
.pipe(
tap(response => console.log(response))
)
));
When queued source item is processed:
Item is mapped to the inner Observable
Subscribes to that inner Observable
**WAITS! for complete
Inner Observable emissions concatenated to the output stream
Unsubscribes from that inner Observable
@deborahkurata
Use concatMap
switchMap
@deborahkurata
switchMap
product$ = this.productSelectedAction$
.pipe(
switchMap(selectedId =>
this.http.get<Product>(`${url}/${selectedId}`)
.pipe(
tap(response => console.log(response))
)
));
On subscribe to the source stream:
Takes in an
Observable stream
Subscribes to that stream
Creates an
output stream
@deborahkurata
switchMap
product$ = this.productSelectedAction$
.pipe(
switchMap(selectedId =>
this.http.get<Product>(`${url}/${selectedId}`)
.pipe(
tap(response => console.log(response))
)
));
Item is mapped to the inner Observable
**Unsubscribes from prior inner Observable
Subscribes to new inner Observable
Inner Observable emissions concatenated to the output stream
Unsubscribes from that inner Observable
When an item is emitted from the source stream:
@deborahkurata
Use switchMap
Higher-order Mapping Operators
mergeMap (flatMap)
concatMap
switchMap
Quiz?
@deborahkurata
QUIZ: Quick Selection
2
1
3
product$ = this.productSelectedAction$
.pipe(
XXXMap(selectedId =>
this.http.get<Product>(`${url}/${selectedId}`)
.pipe(
tap(response => console.log(response))
)
));
@deborahkurata
Quick Selection: mergeMap
2
1
3
Get 2
Get 1
Get 3
@deborahkurata
Quick Selection: concatMap
2
1
3
Get 2
Get 1
Get 3
@deborahkurata
Quick Selection: switchMap
2
1
3
Get 3
@deborahkurata
QUIZ: Quick Selection
product$ = this.productSelectedAction$
.pipe(
switchMap(selectedId =>
this.http.get<Product>(`${url}/${selectedId}`)
.pipe(
tap(response => console.log(response))
)
));
✔
@deborahkurata
QUIZ: Related Data
suppliers$ = from(product.supplierIds)
.pipe(
XXXMap(supplierId =>
this.http.get<Supplier>(`${url}/${supplierId}`)
.pipe(
tap(response => console.log(response))
),
),
toArray()
);
@deborahkurata
Related Data: mergeMap
Get
Get
@deborahkurata
Related Data: concatMap
Get
Get
@deborahkurata
Related Data: switchMap
Get
@deborahkurata
QUIZ: Related Data
suppliers$ = from(product.supplierIds)
.pipe(
mergeMap(supplierId =>
this.http.get<Supplier>(`${url}/${supplierId}`)
.pipe(
tap(response => console.log(response))
),
),
toArray()
);
✔
@deborahkurata
Thank You!
Slides:
http://bit.ly/deborahk-ngconf2020
Code:
https://github.com/DeborahK/Angular-HigherOrderMapping
https://stackblitz.com/edit/angular-todos-deborahk-higher-order
@deborahkurata