繁体   English   中英

缓存 state 到 Map 的 RxJS 主题在 ZC31C335EF37283C451B18BA0 服务中

[英]Cache state in Map of RxJS Subjects by id in Angular service

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

@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 函数可以在多个ownerIdWidgetServiceReplaySubject next() 在上面的示例中,为了简洁起见,我使用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()更新(如在TODO评论)。

关于如何解决这个问题的任何建议,以便消费者继续从WidgetServiceReplaySubject获得更新? 谢谢。

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

我认为这里的主要问题是:

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

httpClient创建一个 finity Observable 意味着一旦请求完成或失败,它将完成。

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM