在带有异步管道的 *ngFor 中使用带有 http-call 的函数时的无限循环

vad*_*shb 5 asynchronous rxjs typescript angular

我在 *ngFor 语句中调用函数:

@Component({
  selector: 'foo-view',
  template: '<div *ngFor="let foo of loadAll() | async"></div>'
})
export class FooComponent {

  loadAll() : Observable<Foo[]> {
    return this.http.get(`api/foos`)
      .map(response => response.json() as Foo[]);
  }

}
Run Code Online (Sandbox Code Playgroud)

当代码启动时,它会一遍又一遍地无限循环发送 http 请求。

为什么?我该怎么做才能避免这种情况?

PS我知道标准的解决方法

@Component({
  selector: 'foo-view',
  template: '<div *ngFor="let foo of foos"></div>'
})
export class FooComponent implements OnInit {

  foos: Foo[] = [];

  ngOnInit() {
    loadAll().subscribe(foos => this.foos = foos);
  }

  loadAll() : Observable<Foo[]> {
    return this.http.get(`api/foos`)
      .map(response => response.json() as Foo[]);
  }

}
Run Code Online (Sandbox Code Playgroud)

但我正在寻找删除多余变量的方法。

mar*_*tin 5

这不是无限循环。每次 Angular 运行更改检测器以检查是否有任何绑定发生更改时,它都需要运行loadAll()进行 HTTP 调用的方法。这是因为它不能确定它在上次检查时没有改变。你显然不想要这个。它需要检查更改的频率很可能也取决于其他组件(例如它的父组件)。

避免这种情况的一种方法正是您通过创建 property 所展示的foos: Foo[]

如果您不想使用另一个状态变量,您可以创建一个 Observable 链来重放缓存数据:

private cached;

ngOnInit() { 
  this.cached = this.http.get(`api/foos`)
    .map(response => response.json() as Foo[])
    .publishReplay(1)
    .refCount()
    .take(1);
}
Run Code Online (Sandbox Code Playgroud)

然后在您的模板中,您可以只使用:

<div *ngFor="let foo of cached | async"></div>
Run Code Online (Sandbox Code Playgroud)

现在,它在开始时只会发出一个请求,每次有人订阅它时,它都会重播该值并完成。

此外,由于RxJS 5.4.0可以使用shareReplay(1)的替代.publishReplay(1).refCount()

顺便说一句,您还可以更改具有changeDetection属性的组件上的更改检测策略以手动运行更改检测。参见ChangeDetectionStrategy