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

Posted

技术标签:

【中文标题】ngFor 块内模板中的异步管道触发 http GET 调用循环【英文标题】:Async Pipe in Template inside ngFor block triggers http GET calls loop 【发布时间】:2018-07-26 13:11:58 【问题描述】:

我有以下组件模板:

<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&lt;Date&gt; 的简单方法:

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

我的目标是调用该方法并使用模板中的异步管道订阅 observable。但是,当我运行应用程序时,会生成无休止的 GET 和 OPTIONS 请求。

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

为什么方法只调用一次,订阅后就不再产生调用?

【问题讨论】:

【参考方案1】:

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

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>

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

【讨论】:

“在模板中调用函数通常不是一个好主意,因为它会导致不可预测的结果。” - 什么是不可预测结果的示例? @Zze 一个不可预知的结果就是例如问题中的问题:) 大声笑,正是:D @VictorNoël 正如您的回答中所解释的那样,这并非不可预测。尽管不幸的是,它的工作方式与预期的完全一样 - 导致此问题的循环变化检测。虽然这是公认的答案,但在这个答案中,首先没有解释实际问题是什么。 @Zze 你是对的,这是不可预测的,因为它永远不会像在模板中使用函数的开发人员所期望的那样。无论如何,感谢您的信任投票!【参考方案2】:

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

【讨论】:

【参考方案3】:

您的问题肯定与变更检测有关。

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

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

确保在不需要时不会触发更改检测(例如,通过使您的组件遵循OnPushChangeDetectionStrategy),但它主要仅在触发组件更新的@Input() 的有限集合时才有效. 只在ngOnInitngOnChanges 中执行一次请求(如果data 是您的组件的@Input()),并将结果存储在一个数组中,您的模板基于该数组执行循环(我会走这条路)。

【讨论】:

以上是关于ngFor 块内模板中的异步管道触发 http GET 调用循环的主要内容,如果未能解决你的问题,请参考以下文章

Angular *ngFor 使用异步管道绑定到 observable - 发生了啥?

Ionic 4 Angular模板与异步管道绑定到可观察

在管道转换后获取 ngFor 中的数组长度

Angular 5:无法使用异步管道更新模板

带有 ngSwitch 的条件按钮也带有来自异步管道数组的 ngFor

找不到“object”类型的不同支持对象“[object Object]”。 NgFor 仅支持绑定到 Iterables,例如带有异步管道的数组