Utilizzo di MergeMap con SwitchMap per ottenere risultati da NgRx Store

Oct 23 2020

Ho 3 selettori:

  1. getUserInfo=> A prendere acnt dettagli (esempio di output: {acntId: 'A1'})
  2. getAllDepartments=> Per andare a prendere l'elenco di tutti dept Ids (esempio di output: ['d1','d2'])
  3. 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

2 martin Oct 23 2020 at 14:55

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.

MrkSef Oct 24 2020 at 00:04

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);