Zum Hauptinhalt springen
Echtzeit-Radar & Feeds
Alle RSS Feeds ➔
👥 Community & Social
••••••••••••••••••••
Intelligence View
⚡ tsecurity.de Intelligence

RxJS in Angular — Chapter 4 | switchMap, mergeMap, concatMap — Observables Inside Observables

👋 Welcome to Chapter 4! Buckle up — this is the chapter that trips up even experienced developers. But don't worry. By the end, it will make total sense. The qu…

Beitrag
0
Seite
0
↗ Quelle (dev.to)
Social ReaktionenReagiere als Erste:r — dein Feedback zählt!




👋 Welcome to Chapter 4!



Buckle up — this is the chapter that trips up even experienced developers. But don't worry. By the end, it will make total sense.



The question today is: What do you do when you have an Observable that produces values… and each value needs to trigger ANOTHER Observable?



Sounds weird? Let's make it concrete.









🤔 The Problem — Observable Inside Observable



Imagine this scenario:




You have a search box. Every time the user types, you want to search the API for results.




The user typing = Observable (stream of text values)

The API call = another Observable



So you have an Observable that needs to trigger another Observable. This is called a higher-order Observable — an Observable of Observables.




// What you WANT to do (but this DOESN'T work properly):
searchInput.valueChanges
.pipe(
map(searchTerm => this.http.get(`/api/search?q=${searchTerm}`))
// ❌ Now you have an Observable of Observables! 😱
// subscribe would give you Observable objects, not the actual data!
)
.subscribe(result => {
// result is an Observable, not the search results!
console.log(result); // Observable { ... } ← NOT what we want
});






To fix this, we need flattening operators — switchMap, mergeMap, and concatMap.



They all do the same basic thing: subscribe to the inner Observable automatically and give you the final result.



But they behave very differently when multiple requests overlap.









🔀 The Three Brothers — An Analogy



Imagine you're asking a waiter to bring you food from 3 different restaurants.



switchMap — "Actually, cancel that last order. I changed my mind, bring me this new one instead."

→ Cancels the previous request when a new one comes in



mergeMap — "Yes, get all three orders! Bring them as they're ready, any order."

→ Runs all requests simultaneously, results come in any order



concatMap — "Wait for the first order to arrive, THEN go get the second one, THEN the third."

→ Runs requests one at a time, in order







🔀 switchMap — Cancel the Old, Start the New



switchMap is the most commonly used flattening operator in Angular. Use it when you only care about the most recent request.





Perfect for: Search boxes, autocomplete, navigation triggers





import { switchMap } from 'rxjs/operators';
import { fromEvent } from 'rxjs';

// Every time the user types, CANCEL the old search, start a new one
searchInput.valueChanges
.pipe(
debounceTime(300), // Wait 300ms after typing stops
switchMap(searchTerm =>
// switchMap subscribes to the HTTP call automatically
// If a new value comes in BEFORE this finishes, it CANCELS this request
this.http.get<Product[]>(`/api/products?search=${searchTerm}`)
)
)
.subscribe(products => {
this.searchResults = products; // ✅ Actual products, not an Observable!
});





Why switchMap here?

User types "iph" → request starts. Then they type "ipho" → cancel the "iph" request and start "ipho". No outdated results! No race conditions!





Full Real-World Search Component





import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
import { Observable } from 'rxjs';
import { debounceTime, distinctUntilChanged, switchMap, startWith } from 'rxjs/operators';

@Component({
selector: 'app-search',
template: `
<input [formControl]="searchControl" placeholder="Search products..." />

<div *ngIf="isLoading">Searching... 🔍</div>

<div *ngFor="let product of results$ | async" class="result-item">
<strong>{{ product.name }}</strong>
<span>৳{{ product.price }}</span>
</div>
`

})
export class SearchComponent implements OnInit {

searchControl = new FormControl('');
results$!: Observable<Product[]>;
isLoading = false;

constructor(private productService: ProductService) {}

ngOnInit(): void {
this.results$ = this.searchControl.valueChanges.pipe(
startWith(''), // Start with empty string (show all)
debounceTime(400), // Wait 400ms after typing
distinctUntilChanged(), // Don't search if value didn't change
switchMap(term => {
this.isLoading = true;
return this.productService.search(term || '');
}),
tap(() => this.isLoading = false) // Turn off loading when results arrive
);
}
}









⚡ mergeMap (aka flatMap) — Run Everything Simultaneously



mergeMap starts every new Observable immediately without cancelling old ones. Results come in as they finish — order is not guaranteed.





Perfect for: Parallel uploads, multiple simultaneous requests





import { mergeMap, from } from 'rxjs';

// Upload multiple files AT THE SAME TIME
const files = [file1, file2, file3];

from(files) // from() converts an array to an Observable
.pipe(
mergeMap(file => this.uploadService.upload(file))
// All 3 uploads start at the same time!
// Results come in as each one finishes
)
.subscribe(uploadResult => {
console.log('Upload complete:', uploadResult.fileName);
});







Real Example — Load User Profiles in Parallel





// You have an array of user IDs, and want to load all profiles at once
const userIds = [1, 2, 3, 4, 5];

from(userIds)
.pipe(
mergeMap(id => this.http.get<User>(`/api/users/${id}`))
// All 5 HTTP calls fire simultaneously!
)
.subscribe(user => {
this.users.push(user);
// Users appear as they load — might be out of order (5 before 3, etc.)
});






⚠️ Warning: mergeMap can overwhelm your server if you have too many items. Use mergeMap(fn, 3) to limit concurrent requests to 3 at a time.








🚶 concatMap — One at a Time, In Order



concatMap waits for each Observable to complete before starting the next one. Results are always in order.





Perfect for: Sequential operations, guaranteed ordering





import { concatMap, from } from 'rxjs';

// Process steps one at a time, in order
const steps = ['step1', 'step2', 'step3'];

from(steps)
.pipe(
concatMap(step => this.processStep(step))
// step1 must complete before step2 starts
// step2 must complete before step3 starts
)
.subscribe(result => {
console.log('Step completed:', result);
});







Real Example — Onboarding Steps in Order





// After registration, run these steps IN ORDER:
// 1. Create user account
// 2. Send welcome email
// 3. Set up default preferences

this.registrationService.register(formData)
.pipe(
concatMap(user => this.emailService.sendWelcome(user.email)),
concatMap(() => this.prefService.setupDefaults())
)
.subscribe(() => {
this.router.navigate(['/dashboard']);
});





Here, the email is sent only after account creation succeeds. Preferences are set only after the email is sent. Perfect ordering! ✅







🆚 Side-by-Side Comparison



Let's say you click a button 3 times quickly (A, B, C), and each click triggers an API call.




Click A starts → [Request A takes 3 seconds]
Click B starts → [Request B takes 1 second]
Click C starts → [Request C takes 2 seconds]






switchMap — Cancels previous




A starts → B starts (A cancelled) → C starts (B cancelled) → C finishes ✅
You get: only C's result






mergeMap — All run at once




A, B, C all run simultaneously
B finishes first → A finishes → C finishes
You get: B, A, C (out of order!)






concatMap — One at a time in order




A starts → A finishes → B starts → B finishes → C starts → C finishes
You get: A, B, C (in perfect order, but slowest)











This example uses switchMap to load a product, then mergeMap to load related items in parallel:






product-detail.component.ts






import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Observable, EMPTY } from 'rxjs';
import { switchMap, mergeMap, toArray, catchError } from 'rxjs/operators';
import { from } from 'rxjs';

@Component({
selector: 'app-product-detail',
template: `
<div *ngIf="product$ | async as product">
<h1>{{ product.name }}</h1>
<p>{{ product.description }}</p>
<p class="price">৳{{ product.price }}</p>

<h2>Related Products</h2>
<div *ngFor="let related of relatedProducts$ | async">
{{ related.name }} — ৳{{ related.price }}
</div>
</div>
`

})
export class ProductDetailComponent implements OnInit {

product$!: Observable<Product>;
relatedProducts$!: Observable<Product[]>;

constructor(
private route: ActivatedRoute,
private productService: ProductService
) {}

ngOnInit(): void {

// switchMap: When the route param changes, cancel old request, load new product
this.product$ = this.route.params.pipe(
switchMap(params => {
const productId = params['id'];
return this.productService.getProduct(productId);
}),
catchError(err => {
console.error('Failed to load product', err);
return EMPTY; // Return empty Observable on error
})
);

// Load related products in parallel using mergeMap
this.relatedProducts$ = this.product$.pipe(
switchMap(product =>
// Get all related IDs and load them simultaneously
from(product.relatedIds).pipe(
mergeMap(id => this.productService.getProduct(id)),
toArray() // Collect all results into one array
)
)
);
}
}












🚦 Route-Based Loading with switchMap



One of the most common real-world uses of switchMap in Angular is loading data based on route parameters:




@Component({ ... })
export class UserProfileComponent implements OnInit {

user$!: Observable<User>;
posts$!: Observable<Post[]>;

constructor(
private route: ActivatedRoute,
private userService: UserService
) {}

ngOnInit(): void {
// Every time the URL changes (e.g., /users/1 → /users/2)
// switchMap cancels the old request and starts a new one
this.user$ = this.route.paramMap.pipe(
switchMap(params => {
const userId = params.get('id')!;
return this.userService.getUser(userId);
})
);

// Load user's posts whenever the user changes
this.posts$ = this.user$.pipe(
switchMap(user => this.userService.getPosts(user.id))
);
}
}






This is incredibly elegant! When the user navigates to /users/3, the old request for /users/2 gets automatically cancelled. No stale data. ✨









🧠 Quick Decision Guide



Use switchMap when:




  • Search box / autocomplete

  • Loading data from route params

  • You only care about the latest request

  • Cancelling outdated requests is desirable



Use mergeMap when:




  • Parallel uploads

  • Loading multiple independent items simultaneously

  • Order doesn't matter

  • You want maximum speed



Use concatMap when:




  • Steps must happen in order

  • Sequential processing

  • Like a queue — first in, first out









🧠 Chapter 4 Summary — What You Learned




  • Sometimes you need an Observable that triggers another Observable — called a higher-order Observable


  • switchMap — cancels the previous inner Observable when a new one starts (best for search)


  • mergeMap — runs all inner Observables simultaneously (best for parallel tasks)


  • concatMap — runs inner Observables one at a time in order (best for sequential steps)


  • switchMap is the most commonly used in Angular — especially for HTTP + route params

  • All three operators automatically subscribe to the inner Observable and give you the unwrapped result









📚 Coming Up in Chapter 5...



We've covered transformation operators. Now let's learn about Subject — the two-way radio of RxJS.



A Subject is an Observable that you can manually push values into. It's essential for sharing data between components and services!



See you in Chapter 5! 🚀






💌 RxJS Deep Dive Newsletter Series | Chapter 4 of 10



Follow me on : Github Linkedin Threads Youtube Channel

2. Cyber Threat Intelligence & Forensik

CTI Threat Relationship Graph2 Knoten / 1 Relationen
CVE / Incident Software MITRE ATT&CK CWE Weakness IoC
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten RxJS in Angular — Chapter 4 | switchMap, mergeMap, concatMap — Observables Inside Observables

Thematisch verwandte Begriffe: RxJS, Angular, Chapter, switchMap · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

💬 Kommentare werden geladen…
Zum Aktualisieren ziehen
tsecurity.de Icon
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag