Utilizzo di MergeMap con SwitchMap per ottenere risultati da NgRx Store
Ho 3 selettori:
getUserInfo=> A prendere acnt dettagli (esempio di output:{acntId: 'A1'})getAllDepartments=> Per andare a prendere l'elenco di tutti dept Ids (esempio di output:['d1','d2'])getAllDeptManagers=> per ottenere l'elenco dei responsabili di reparto per ogni ID reparto.
Ora, ho scritto di seguito il codice:
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);
})
);
Secondo la mia comprensione, mergeMapprende array e di conseguenza chiama a functione appiattisce l'array di osservabile che viene restituito.
Ricevo ['d1','d2']ogni chiamata al selettore getAllDeptManagers. quello che mi aspetto è d1e poi d2e così via, e poi ottieni tutte le risposte in una volta deptsdalla console.
Per favore aiuto
Risposte
mergeMapnon appiattisce l'output dal suo Observable interno. Si riemette solo quando l'osservabile interno emette e basta. Quindi sembra che tu voglia usare forkJoinqui:
mergeMap(deptIds => forkJoin(
deptIds.map(deptId => this.store.select(getDepartmentManagers, {
departmentId: deptId
}).pipe(take(1)))
)),
Quindi gli osservatori riceveranno una singola serie di risultati per deptIdsperché forkJoinaspetteranno fino al completamento di tutti gli osservatori interni.
Il modo più semplice per ottenere ciò che desideri è mappare il tuo array in un flusso. Il primo switchMapprende un valore e lo mappa su un flusso. Quel flusso emette un array, quindi te ne serve solo uno in più mergeMap. Come questo:
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);
Quella mergeMap sembra divertente (il bit in più) , ma se restituisci un array, viene convertito in un flusso. Semanticamente è lo stesso mergeMap(depIdArr => from(depIdArr))ma è un po 'più performante.
Se trasformando un array in un ruscello e di nuovo in un array è troppo, è possibile combinare questi passi con zip(), combineLatest()o che cosa potrebbe essere migliore in questo caso: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);