Reputation: 16565
I have an observable that is doing its job and working fine. However it has several layers of nesting that doesn't feel very RxJS'y and seem like they could be flattened
const filteredEntries$ = this.filterForm.controls.day.valueChanges.pipe(
switchMap((selectedDayId: string) =>
this.entries$.pipe(
map((entries: Entry[]) =>
this.entriesForDay$(entries, selectedDayId)
)
)
)
)
I'd like to remove the inner nested pipe so that things stay clearer:
const filteredEntries$ = this.filterForm.controls.day.valueChanges.pipe(
switchMap((selectedDayId: string) =>
this.entries$
),
map((entries: Entry[]) =>
// selectedDayId is not available in this scope :( 🛑
this.entriesForDay$(entries, selectedDayId)
)
)
Is it possible to flatten the observable out like this and if so how can I pass the value of selectedDayId
into the final .map
statement?
Plot twist...
There's nothing like writing a SO question to make you answer it. I guess the solution is that you pass the selectedDayId
on and return it from the first switch statement. So my question then becomes how can you do that elegantly rather than fumbling around with arrays or objects. Is there some way to mass assign the return values directly into the next method's parameters (as shown below):
const filteredEntries$ = this.filterForm.controls.day.valueChanges.pipe(
switchMap((selectedDayId: string) =>
this.entries$, selectedDayId // is there a way to return two values
// directly to the next method?
),
map((entries: Entry[], selectedDayId) =>
this.entriesForDay$(entries, selectedDayId)
)
)
Upvotes: 1
Views: 275
Reputation: 14089
You don't need switchMap
in your case. You could combine the valueChanges
and entries$
streams.
const filteredEntries$ = combineLatest([
this.filterForm.controls.day.valueChanges,
this.entries$
]).pipe(
map(([selectedDayId, entries]) =>
this.entriesForDay$(entries, selectedDayId)
)
)
In general I think having one nested layer is perfectly fine. If the nesting becomes to deep I would suggest moving some of the code into extra functions.
const filteredEntries$ = this.filterForm.controls.day.valueChanges.pipe(
switchMap((selectedDayId: string) => this.entriesFor$(selectedDayId))
)
const entriesFor$ = (selectedDayId: string) => this.entries$.pipe(
map((entries: Entry[]) => this.entriesForDay$(entries, selectedDayId))
)
Upvotes: 1
Reputation: 1138
One possible solution is to use combineLatest
:
When any observable emits a value, emit the last emitted value from each
Your code would be:
const filteredEntries$ = this.filterForm.controls.day.valueChanges.pipe(
switchMap((selectedDayId: string) =>
combineLatest(this.entries$, of(selectedDayId))
),
map((entries: Entry[], selectedDayId) =>
this.entriesForDay$(entries, selectedDayId)
)
)
Upvotes: 1