2

I'm working with angularfire and I'm getting a continuous stream of arrays with mission ids in it. I need to fetch the mission document of each id in the array as a new observable. Then return an array of mission docs into the stream so that I'll be able to subscribe to it in my component and display a list of missions.

So far I got it to work with mergeMap. I split the array and fetch the mission docs and return them into the stream. Only problem with my solution is, that when I subscribe to the Observable I don't get an array of missions but every mission as a single change which I can not loop with ngFor. using the toArray() operator does in this case not work, because its a continuous stream which never ends.

This is my code so far:

this.db.collection(`games/${gameId}/missions`).valueChanges().pipe(
    mergeMap(missions => missions),
    mergeMap((mission: any) => {
        return this.db.doc(`missions/${mission.id}`).snapshotChanges();              
    }),
);

This generates the following output in single events:

{ id: 1, missionProperties }
{ id: 2, missionProperties }
{ id: 3, missionProperties }

But I would like to have it in one event as array of missions:

[
    { id: 1, missionProperties },
    { id: 2, missionProperties },
    { id: 3, missionProperties }
]
0

2 Answers 2

1

use the scan operator to aggregate

this.db.collection(`games/${gameId}/missions`).valueChanges().pipe(
  switchMap(missions =>
      from(missions).pipe(
        mergeMap(mission => this.db.doc(`missions/${mission.id}`).snapshotChanges()),
       scan((acc, curr) => [curr, ...acc], [])
      ),
)
Sign up to request clarification or add additional context in comments.

7 Comments

Thank you @FanCheung, this works and adds every mission to the an array but when something changes in firebase I get all ids agin and have the same missions multiple times in the array. Should I move the scan to the inner observable or so?
If you change the first mergeMap to switchMap it shall work. It will cancel inner observable when source observable emits. I updated the answer
Ah I see! This makes sense, BUT as often with rxjs it does not what you would expect… Still the same issue.
Just refactor your code and move scan to inner observable. that should work now. Forgot scan is accumulating everything.
But that way you do not loop over the array anymore. SwitchMap gives us the the array of ids from every event which needs to be looped to fetch the associated data and merge it then.
|
1

You can use a buffer operator https://www.learnrxjs.io/operators/transformation/buffer.html

of(1, 2, 3, 4, 5).pipe(
  bufferCount(5),
).subscribe(x => console.log(x)); // prints [1, 2, 3, 4, 5]

Edited:

I just saw the toArray() operator:

of(1, 2, 3, 4, 5).pipe(
  toArray(),
).subscribe(x => console.log(x));

Edited 2:

of(1, 2, 3, 4, 5).pipe(
  scan((acc, curr) => { acc.push(curr); return acc; }, []),
).subscribe(x => console.log(x)); // prints [1] [1,2] [1,2,3]....

5 Comments

Thank you @MoxxiManagarm but how do I know how large to set the buffer? The number of mission can vary.
Maybe I was unclear, but you can use a buffer. I mean there are several buffer operators. You can choose which of thos fits the best for you. But I just saw there is a more suitable one for you :-) Edited my answer
buffer seems not to work in this case, because its a continuous stream which never ends. So the buffer is never released.
buffer is released when the condition fits even before the Observable is completed. of(1, 2, 3, 4, 5).pipe(bufferCount(2)) would emit [1,2] [3,4] [5]. But you are right for toArray() I guess
Based on this you might want to think about scan, I edited my answer again

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.