Reputation: 1426
I have 3 selectors:
getUserInfo
=> to fetch acnt details (sample output : {acntId: 'A1'}
)getAllDepartments
=> to fetch list of all dept Ids (sample Output: ['d1','d2']
)getAllDeptManagers
=> to get list of dept managers for each Dept Id.Now, I have written below code:
this.store
.select(getUserInfo)
.pipe(
switchMap((res) => this.store.select(getAllDepartments, { account: res.acntId})),
mergeMap(deptId => this.store.select(getDepartmentManagers,{departmentId: deptId }))
)
.subscribe((depts) => {
console.log(depts);
})
);
As per my understanding, mergeMap
takes array and accordingly call a function
and flatten the array of observable which is returned.
I am getting ['d1','d2']
in every call to selector getAllDeptManagers
. what I am expecting is d1
and then d2
and so on, and then get all response in one go as depts
of console.
Please help
Upvotes: 1
Views: 935
Reputation: 8062
The simplest way to achieve what you want is to map your array into a stream. The first switchMap
takes a value and maps it onto a stream. That stream emits an array, so you just need one more mergeMap
. Like this:
this.store.select(getUserInfo).pipe(
switchMap(res => this.store.select(getAllDepartments, { account: res.acntId})),
mergeMap(depIdArr => depIdArr), // <- The extra bit
map(deptId => this.store.select(getDepartmentManagers,{departmentId: deptId })),
// Really, your source observables should be competeing,
// but if they don't take(1) should ensure that they do.
mergeMap(depMan$ => depMan$.pipe(take(1))),
toArray()
).subscribe(console.log);
That mergeMap looks funny (the extra bit), but if you return an array it gets converted into a stream. It's semantically the same as mergeMap(depIdArr => from(depIdArr))
but it's a bit more performant.
If turning an array into a stream and back into an array is too much, you can combine those steps with zip()
, combineLatest()
, or what might be best in this case: forkJoin()
this.store.select(getUserInfo).pipe(
switchMap(res => this.store.select(getAllDepartments, { account: res.acntId})),
map(deptIdArr => deptIdArr.map(
deptId => this.store.select(
getDepartmentManagers,{departmentId: deptId }
)
)),
// Really, your source observables should be completing,
// but if they don't take(1) should ensure that they do.
map(deptIdArrS => deptIdArrS.map(
deptId$ => deptId$.pipe(take(1))
),
mergeMap(deptIdArrS => forkJoin(deptIdArrS))
).subscribe(console.log);
Upvotes: 0
Reputation: 96969
mergeMap
doesn't flatten the output from its inner Observable. It just reemits when the inner Observable emits and that's it. So it looks like you want to use forkJoin
here:
mergeMap(deptIds => forkJoin(
deptIds.map(deptId => this.store.select(getDepartmentManagers, {
departmentId: deptId
}).pipe(take(1)))
)),
Then observers will receive a single array of results for deptIds
because forkJoin
will wait until all of the inner Observavbles complete.
Upvotes: 2