我正在尝试一般性地学习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()
将推送重复项。
我已经阅读了其中一些运算符的文档,并且对它们有一定的了解,但不足以弄清楚为什么此代码不符合应有的方式。
答案 0 :(得分:1)
首先,switchMap
希望您返回一个Observable。如果它是类似数组的值,它将使用from
(请参见from
example)将其转换为数组。
如果您真的想让数组返回流,则应返回一个流,例如使用of
:switchMap(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()
中的值。最常见的是:forkJoin
和zip
希望这会有所帮助