使用 Rxjs 构建 Angular 商店
Build Angular Store with Rxjs
我有这个 Angular 应用程序,我想在其中为聊天对象创建自定义商店。
(codesandbox: https://codesandbox.io/s/chat-store-yw4yz)
Note: I know there are tools like ngrx but this is part of a bigger project and I would prefer to use this custom implementation so I understand better the behaviour of the store.
上下文
使其快速高效的想法是将两种类型的记录放入存储中:
- 具有
key = chat id
和 value = chat object
的字典(作为地图)
- 用于按 groupId 对聊天进行分组的索引(作为地图),其中
key = groupId
和 value = chatId
所以我有这个接口定义:
export interface ChatStoreModel {
chats: Map<number, Chat>;
chatsByGroup: Map<number, number[]>;
}
当聊天对象保存到商店时,应该发生两件事:
- 聊天对象已保存到商店中
chats
- chat id保存到store
chatsByGroup
对应的groupId
问题
无法从群组中获取聊天对象。我执行以下操作:
- 使用 groupId
从商店 chatsByGroup
获取聊天 ID 列表
- 对于每个聊天 ID,从存储中获取元素
chats
代码:
getGroupChats$(groupId: number): Observable<Chat[]> {
return this._store.select("chatsByGroup").pipe(
map((chatsByGroup) => chatsByGroup.get(groupId) || []),
switchMap((ids) => {
if (!ids || !ids.length) {
return of([]);
}
const chatsList$: Observable<Chat>[] = ids.map((id) =>
this._store.select("chats").pipe(map((chats) => chats.get(id)))
);
return forkJoin([...chatsList$]).pipe(
map((list) => {
return ([] as Chat[]).concat(...list);
})
);
}),
shareReplay(1)
);
}
调试这段代码,它到达 return forkJoin
行并且 ids 有聊天列表,但它从未到达 map((list) => {
行,所以应用程序不显示聊天列表。
任何帮助将不胜感激,谢谢!
whenever any of that observables completes without emitting any value,
forkJoin will complete at that moment as well and it will not emit
anything either
这是来自 RxJS ForkJoin Doc。我的猜测是,您传递给 forkJoin
的 Observable 之一在没有发出任何东西的情况下完成,因此 forkJoin
立即完成而没有发出任何东西。
尝试使用 defaultIfEmpty
运算符以确保这种情况不会发生。我建议这样做:
const chatsList$: Observable<Chat>[] = ids.map((id) =>
this._store.select("chats").pipe(map((chats) => chats.get(id))),
defaultIfEmpty(null),
);
forkJoin will wait for all passed observables to complete
这又是来自文档。这意味着您传递给 forkJoin
的所有 Observable 都必须完成,以便 forkJoin
自己发出值。
我的猜测是 this._store.select("chats").pipe(map((chats) => chats.get(id))),
这一行将无休止地 运行 所以你需要提供 take(1)
才能完成,否则 forkJoin
不会发出任何东西.所以尝试这样的事情:
const chatsList$: Observable<Chat>[] = ids.map((id) =>
this._store.select("chats").pipe(map((chats) => chats.get(id))),
take(1),
defaultIfEmpty(null),
);
最后,如果你不想使用 take(1)
,我的意思是你想订阅 Observable,只要它发出值,你可能想使用 combineLatest
而不是 forkJoin
。那是因为 forkJoin
等待完成发出一些东西,而 combineLatest
将发出提供的 Observables 的最新值。
我有这个 Angular 应用程序,我想在其中为聊天对象创建自定义商店。
(codesandbox: https://codesandbox.io/s/chat-store-yw4yz)
Note: I know there are tools like ngrx but this is part of a bigger project and I would prefer to use this custom implementation so I understand better the behaviour of the store.
上下文
使其快速高效的想法是将两种类型的记录放入存储中:
- 具有
key = chat id
和value = chat object
的字典(作为地图)
- 用于按 groupId 对聊天进行分组的索引(作为地图),其中
key = groupId
和value = chatId
所以我有这个接口定义:
export interface ChatStoreModel {
chats: Map<number, Chat>;
chatsByGroup: Map<number, number[]>;
}
当聊天对象保存到商店时,应该发生两件事:
- 聊天对象已保存到商店中
chats
- chat id保存到store
chatsByGroup
对应的groupId
问题
无法从群组中获取聊天对象。我执行以下操作:
- 使用 groupId 从商店
- 对于每个聊天 ID,从存储中获取元素
chats
chatsByGroup
获取聊天 ID 列表
代码:
getGroupChats$(groupId: number): Observable<Chat[]> {
return this._store.select("chatsByGroup").pipe(
map((chatsByGroup) => chatsByGroup.get(groupId) || []),
switchMap((ids) => {
if (!ids || !ids.length) {
return of([]);
}
const chatsList$: Observable<Chat>[] = ids.map((id) =>
this._store.select("chats").pipe(map((chats) => chats.get(id)))
);
return forkJoin([...chatsList$]).pipe(
map((list) => {
return ([] as Chat[]).concat(...list);
})
);
}),
shareReplay(1)
);
}
调试这段代码,它到达 return forkJoin
行并且 ids 有聊天列表,但它从未到达 map((list) => {
行,所以应用程序不显示聊天列表。
任何帮助将不胜感激,谢谢!
whenever any of that observables completes without emitting any value, forkJoin will complete at that moment as well and it will not emit anything either
这是来自 RxJS ForkJoin Doc。我的猜测是,您传递给 forkJoin
的 Observable 之一在没有发出任何东西的情况下完成,因此 forkJoin
立即完成而没有发出任何东西。
尝试使用 defaultIfEmpty
运算符以确保这种情况不会发生。我建议这样做:
const chatsList$: Observable<Chat>[] = ids.map((id) =>
this._store.select("chats").pipe(map((chats) => chats.get(id))),
defaultIfEmpty(null),
);
forkJoin will wait for all passed observables to complete
这又是来自文档。这意味着您传递给 forkJoin
的所有 Observable 都必须完成,以便 forkJoin
自己发出值。
我的猜测是 this._store.select("chats").pipe(map((chats) => chats.get(id))),
这一行将无休止地 运行 所以你需要提供 take(1)
才能完成,否则 forkJoin
不会发出任何东西.所以尝试这样的事情:
const chatsList$: Observable<Chat>[] = ids.map((id) =>
this._store.select("chats").pipe(map((chats) => chats.get(id))),
take(1),
defaultIfEmpty(null),
);
最后,如果你不想使用 take(1)
,我的意思是你想订阅 Observable,只要它发出值,你可能想使用 combineLatest
而不是 forkJoin
。那是因为 forkJoin
等待完成发出一些东西,而 combineLatest
将发出提供的 Observables 的最新值。