使用 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.

上下文

使其快速高效的想法是将两种类型的记录放入存储中:

所以我有这个接口定义:

export interface ChatStoreModel {
  chats: Map<number, Chat>;
  chatsByGroup: Map<number, number[]>;
}

当聊天对象保存到商店时,应该发生两件事:

问题

无法从群组中获取聊天对象。我执行以下操作:

代码:

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 的最新值。