我最近一直在使用 Angular,但仍然对 RxJs 库没有信心,所以我请求你的帮助。我尝试了很多选项,但没有任何效果。发生的最好的事情 - 我将在下面给出一个例子。
挑战:需要在 Angular Web 应用程序中每 5 秒更新一次数据。对于数据输出,我使用 AsyncPipe。我不知道如何设置它。如果我在 ngOnInit 中接收到一个 Observable 对象一次并将其传递给异步管道,则所有数据都显示成功,但是当服务器上的数据发生变化时,客户端上的数据没有更新(这是合乎逻辑的。你需要拉服务器)。如果我使用计时器,客户端可以看到刷新不断发生。我有一种感觉,不是将新的 Observable 对象分配给 tablesOrders$ 变量,而是需要使用某种 RxJs 运算符而不是这样的计时器,这样我就不会更改对对象的引用,而是更新当前对象使用新数据(类似于间隔,但我无法应用它)。
TS:
export class TablesComponent implements OnInit, OnDestroy {
@Input() myLocation$: Observable<Location>;
@Input() myTables$: Observable<Table[]>;
locationId: string;
tablesOrders$: Observable<TableOrders[]> = new Observable<TableOrders[]>();
private _onDestroy = new Subject<void>();
loading: boolean = true;
constructor(private element: ElementRef, public dialog: MatDialog,
private operations: TableOperationsService, private router: Router,
private snackBar: MatSnackBar) { }
ngOnInit() {
this.myLocation$.pipe(takeUntil(this._onDestroy)).subscribe(result => {
if (isNullOrUndefined(result.locationId)) { return };
this.locationId = result.locationId.toString();
this.updateOrdersTask();
});
}
updateOrdersTask() {
timer(0,5000).subscribe(_=>{
let orders$ = this.operations.getOrders(this.locationId)
.pipe(takeUntil(this._onDestroy));
//вот здесь мы каждый раз присваимваем переменной новый объект
this.tablesOrders$ = forkJoin([orders$, this.myTables$])
.pipe(timeout(5000), map(result => {
let tableOrders: TableOrders[] = [];
result[1].forEach(table => {
let tb = new TableOrders(table,
result[0].orders.data.filter(o => o.table.id == table.deviceID), result[0].waiter);
tableOrders.push(tb);
});
this.loading = false;
return tableOrders;
}),
catchError(e => {
this.loading = false;
return of(null);
}));
return this.tablesOrders$;
})
}
HTML:
<mat-progress-bar mode="query" *ngIf="loading"></mat-progress-bar>
<div cdkDropList class="tables-list" (cdkDropListDropped)="drop($event)" *ngIf="!isNullOrUndefined(tablesOrders$ | async); else noTables">
<div class="table-drag-box"
*ngFor="let tableOrders of tablesOrders$ | async"
cdkDragLockAxis="x" cdkDrag cdkDropListData="tableOrders"
(cdkDragStarted)="swipeStarted($event)" (cdkDragMoved)="swipeMoved($event)"
(cdkDragReleased)="swipeEnded($event, tableOrders)" (click)="showOrderDetails(tableOrders)">
<!-- здесь в различных элементах я использую переменную tableOrders которую получил из пайпа-->
</div>
</div>
好吧,让我们试着找出你的问题。这并不容易做到,因为你有意大利面条代码(无意冒犯🤗)。让我们试着用这些意大利面做carbonara :)
下次我会问你粘贴格式化的代码,你粘贴的代码是错误的,如果我复制它并粘贴到prettier中,那么prettier会报错。
因此,我们将逐步完成您的代码,您订阅了
myLocation$,它通过输入参数传递。在内部,您将侦听器传递给事件next,进行检查并调用方法updateOrdersTask。在您订阅的方法timer内部,在事件侦听器内部,next您创建一个作为参数传递给管道的流async。对于未来,我告诉你尽量避免订阅组件。怎么不退订?根本不签。组件中的订阅主要在跟踪路由器事件等情况下需要,在这些情况下
Router.prototype.events与模板没有交互。在与模板交互的地方,只需创建一个线程并使用async管道就足够了。因此,我们需要摆脱订阅,为此我们将通过检查管道
myLocation$继续,也就是说,我们需要替换这段代码:timerundefined对此:
此外,每次线程
myLocation$产生事件,我们都需要订阅timer(0, 5000),它会为我们做switchMap(其他类型的投影映射运算符mergeMap|concatMap|exhaustMap不适合我们):请注意,我
timer映射到locationId以免造成运营商混乱。接下来,在从 - 生成事件之后,
timer我们需要将投影映射到您拥有forkJoin的代码:map请注意,我将整个内容分配给了属性
tablesOrders$,当然不是最好的代码,但它仍然有助于摆脱意大利面条,因为这是没有副作用的代码,您无需担心订阅中的计时器和订阅,以及数据竞争(哪个计时器将更早生成事件等),运算符是一种更明确的方法。还有一个问题-我无法测试此代码,因为您没有提供可重现的示例:(因此,如果有的话,请写在评论中。
对于这样的情况,您需要永无止境的检查,使用异步管道太昂贵了!
尝试添加类似 Socket.io 的内容。使用套接字非常简单,即使您是第一次使用它们,也可以快速学习。