带数组的SwitchMap运算符

时间:2019-04-15 17:45:46

标签: angular rxjs observable

我正在尝试一般性地学习rxjs和Observable概念,并有一种情况,我有<Room>{}类,其中<Player>{}可以加入多对多关系样式。

在Firestore中,我有rooms的集合,其中每个房间都有一个名为players的属性,该属性是用户uid s的数组。

创建房间组件后,我订阅_roomService.getPlayersInRoom(roomId),如下所示:

getPlayersInRoom(roomId: string) {
  return this._db.doc<Room>(`rooms/${roomId}`).valueChanges().pipe(
    map(room => room.players),
    switchMap(players => players),//2
    switchMap(playerId => {
      if(playerId) {
        return this._db.doc<Player>(`users/${playerId}`).valueChanges();
      }
    })
  );
}

我稍后使用

进行订阅
.subscribe(player => {
    if (player) {
      this.players = new Array();
      this.players.push(player);
    }

这里有几个问题。我的观察者未返回预期的玩家数组(请参见// 2将字符串[]转换为字符串的行)

另一个问题是,每次空间发生变化时,我都会在组件中新建this.players数组(否则.push()将推送重复项。

我已经阅读了其中一些运算符的文档,并且对它们有一定的了解,但不足以弄清楚为什么此代码不符合应有的方式。

1 个答案:

答案 0 :(得分:1)

首先,switchMap希望您返回一个Observable。如果它是类似数组的值,它将使用from(请参见from example)将其转换为数组。

如果您真的想让数组返回流,则应返回一个流,例如使用ofswitchMap(value => of([]))

但是,在您的情况下,您想用流替换数组中的每个id。例如,我们将需要使用combineLatest operator(名称不言而喻)。每个玩家阵列都将切换到一个新的流。我们将在valueChanges()流中结合最新值。

这里有个例子:

getPlayersInRoom(roomId: string) {
    return this._db.doc<Room>(`rooms/${roomId}`).valueChanges().pipe(
      map(room => room.players),
      switchMap(players => {
        // turn array of player IDs
        // into array of streams of changes
        const playersStreams = players.map(
          playerId => this._db.doc<Player>(`users/${playerId}`).valueChanges()
        );

        // combine latest changes from all streams in that array
        return combineLatest(...playersStreams);
      })
    );
  }

然后在订阅players中将是valueChanges()的组合值数组。

getPlayersInRoom(5)
  .subscribe(players => {
    this.players = players;
  })

请注意,还有更多的方式可以合并多个valueChanges()中的值。最常见的是:forkJoinzip

还有更多map a value on a stream

的方法

希望这会有所帮助