ngFor 块内模板中的异步管道触发 http GET 调用循环

Async Pipe in Template inside ngFor block triggers http GET calls loop

我有以下组件模板:

<div *ngFor="let ctrl of data; trackBy:ctrl?.Id">
   <div *ngIf="getNext(ctrl.nextDate) | async as next">
        <span>{{next | date: 'dd.MM.yyyy'}}</span>
   </div>
</div>

getNext() 是一个返回 Observable<Date>:

的简单方法
public getNext(deadline: string): Observable<Date> {
   return this.http.get<Date>(`${this.config.apiEndpoint}/api/meeting?deadline=${deadline}`);
}

我的目标是调用该方法并使用模板中的异步管道订阅可观察对象。但是,当我 运行 应用程序生成无穷无尽的 GET 和 OPTIONS 请求时。

另外,如果我将方法调用放在 ngFor 之外,也会发生同样的情况。该调用需要在 ngFor 内部执行,因为每个集合项的参数都不同。

为什么只调用一次,订阅后就不再调用了?

您的问题很可能与变更检测有关。

每次 angular 认为绘制模板所需的内容可能会发生变化(即只要有浏览器事件,除非您的组件是 OnPush),它会重新绘制组件,从而重新触发循环和可观察对象。

在这种情况下,您有两个选择:

  • 确保在不需要时不会触发变更检测(例如让您的组件遵循 OnPush ChangeDetectionStrategy),但它主要仅在 [=13= 的一组有限的情况下才有效] 触发组件的更新。
  • 只在 ngOnInitngOnChanges 中执行一次请求(在 data 是组件的 @Input() 的情况下)并将结果存储在数组中您基于模板执行 for 循环(我会这样做)。

Angular 在每个事件周期调用 getNext,并且每次 getNext 发出新的 http 请求和 returns 新的 Observable。您需要从第一个函数调用中缓存 Observable。我建议您在控制器中的某处创建它们,然后将模板作为变量传入。

在模板中调用函数通常不是一个好主意,因为它会导致不可预知的结果。这是重构代码以避免这种情况的方法:

data: any = [....] // some data
data$: Observable[];

ngOnInit() {
    this.data$ = this.data.map(elem => this.getNext(elem));
} 

public getNext(deadline: string): Observable<Date> {
   return this.http.get<Date>(`${this.config.apiEndpoint}/api/meeting?deadline=${deadline}`);
}

在您的模板中:

<div *ngFor="let ctrl of data$">
   <div *ngIf="ctrl | async as next">
        <span>{{next | date: 'dd.MM.yyyy'}}</span>
   </div>
</div>

这是我创建的一个 stackblitz,您可以在其中看到类似机制的工作原理:https://stackblitz.com/edit/angular-nyn4qz