angular - 在 Angular 服务中通过 id 在 RxJS Subjects Map 中缓存状态

标签 angular rxjs

我为每个 ownerId 收集了一个小部件.我正在尝试使用 Map<string, ReplaySubject<Widget[]>> 在 Angular 服务中缓存这些集合的状态.

@Injectable({
    providedIn: 'root',
})
export class WidgetService {

    private readonly widgetsByOwnerId: Map<string, ReplaySubject<Widget[]>> =
        new Map<string, ReplaySubject<Widget[]>>();

    constructor(
        private readonly httpClient: HttpClient
    ) {}

    getWidgets(ownerId: string): Observable<Widget[]> {
        if (!this.widgetsByOwnerId.has(ownerId)) {
            const widgets$ = this.httpClient.get<Widget[]>(`api/${ownerId}/widgets`);
            const widgetsCache$ = new ReplaySubject<Widget[]>(1);
            widgets$.subscribe(widgetsCache$);
            this.widgetsById.set(ownerId, widgetsCache$);
        }
        return this.widgetsById.get(ownerId).asObservable();
    }

    createWidget(widget: Widget): Observable<Widget> {
        return this.httpClient
            .post<Widget>(`api/${widget.ownerId}/widgets`, widget)
            .pipe(
                tap((createdWidget): void => {
                    this.widgetsByOwnerId.forEach((widgets$, ownerId) =>
                        widgets$.pipe(take(1)).subscribe({
                            next: (widgets: Widget[]) => {
                                if(createdWidget.ownerId == ownerId || isOwnedById) {
                                    widgets.push(createdWidget);
                                    widgets$.next(widgets);
                                }
                            },
                        })
                    );
                }
            );
    }

    //additional CRUD functions excluded for brevity
}

某些特殊类型的小部件可以由多个小部件所有者拥有,因此 WidgetService 中的一些 CRUD 功能可以next()在多个 ownerIdReplaySubject秒。在上面的示例中,为了简洁起见,我使用 isOwnedById 省略了一些逻辑。 .

此服务的消费者如下所示:

@Component({
    selector: 'app-widgets',
    templateUrl: './widgets.component.html',
})
export class WidgetsComponent implements OnInit, OnDestroy {

    widgets: Widget[];

    private readonly ngUnsubscribe$: Subject<unknown> = new Subject();

    constructor(
        private readonly widgetService: WidgetService
    ) {}

    ngOnInit(): void {
        const ownerId = this.getOwnerId();
        this.getWidgets(ownerId);
    }

    ngOnDestroy(): void {
        this.ngUnsubscribe$.next();
        this.ngUnsubscribe$.complete();
    }

    private getOwnerId(): string {
        ...
    }

    private getWidgets(ownerId: string): void {
        this.widgetService
            .getWidgets(ownerId)
            .pipe(takeUntil(this.ngUnsubscribe$))
            .subscribe({
                // TODO: this next is called once when first subscribed, but not when future widgets are created
                next: (widgets) => {
                    this.widgets = widgets;
                },
            });
    }

}

出于某种原因,当消费组件首次初始化时,它会获取缓存的 Widget[]成功地。但是,当像 WidgetService.createWidget() 这样的 CRUD 操作时在其他组件中执行(同时 WidgetComponent 的实例仍然存在,例如在 WidgetComponent.ngOnDestroy() 之前),这个 WidgetComponent消费者永远不会收到 next() -ed 更新(如 TODO 评论中所述)。

有关如何解决此问题的任何建议,以便消费者继续从 WidgetService 获得更新的 ReplaySubject秒?谢谢。

这个问题有点类似于this one ,但差异很大,以至于我无法弄清楚如何使他们的答案适应这个用例。

最佳答案

我认为这里的主要问题在于:

 const widgets$ = this.httpClient.get<Widget[]>(`api/${ownerId}/widgets`);
 const widgetsCache$ = new ReplaySubject<Widget[]>(1);
 widgets$.subscribe(widgetsCache$);

httpClient 创建一个有限的 Observable,这意味着一旦请求完成或失败,它就会完成。

因此,您的 ReplaySubject 作为 Observer 传递给 subscribe 方法将完成,您将不会再收到任何事件。

要修复它,您可以将 subscribe 实现替换为以下内容:

widgets$.subscribe(res => widgetsCache$.next(res));

关于angular - 在 Angular 服务中通过 id 在 RxJS Subjects Map 中缓存状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61737059/

相关文章:

javascript - RxJs - (为什么)Observable 是否在新订阅上发出最新值?

Angular4如何使用flatMap链接forkJoin

angular - 如何避免Angular和Spring boot项目中的CORS问题?

Angular Testing "Office is not defined"

html - 声明忽略 parent 填充的背景

angular - 出错后不要关闭 Observable

javascript - 将值从 mergeLatest 映射到 Array

angular - 如何使用chart.js动态设置轴上的刻度?

Angular 2 - MockBackend - Passtrough 等效吗?

javascript - 处理 rxjs ajax.map 调用中的异常