angular - Oändlig loop när man använder funktion med http-anrop i *ngFor med async-pipe


Jag anropar en funktion i *ngFor-uttrycket:

@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[]);
  }

}

När koden startar skickar den HTTP-förfrågningar i en oändlig loop om och om igen.

Varför? Vad kan jag göra för att undvika detta?

P.S. Jag känner till standardlösningar som

@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[]);
  }

}

men jag letar efter sättet att ta bort överflödig variabel.

Svar #1

Det är inte en oändlig loop. Varje gång Angular kör ändringsdetektorn för att kontrollera om någon av bindningarna har ändrats måste den köra loadAll()-metoden som gör HTTP-anropet. Detta beror på att den inte kan vara säker på att det inte har ändrats sedan den senast kontrollerade. Du vill uppenbarligen inte detta. Hur ofta den behöver kontrollera ändringar kan mycket väl bero på andra komponenter också (dess förälder till exempel).

Ett sätt att undvika detta är exakt vad du visade genom att skapa egenskapen foos: Foo[].

Om du inte vill använda en annan tillståndsvariabel kan du skapa en Observable-kedja som återger den cachelagrade datan:

private cached;

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

Och sedan kan du i din mall bara använda:

<div *ngFor="let foo of cached | async"></div>

Nu kommer det bara att göra en begäran i början och varje gång någon prenumererar på den kommer det bara att spela upp värdet och slutföra.

Dessutom, sedan RxJS 5.4.0 kan du använda shareReplay(1) istället för .publishReplay(1).refCount().

Förresten kan du också ändra Change Detection-strategin på komponenten med changeDetection-egenskapen för att manuellt köra förändringsdetekteringen. Se ChangeDetectionStrategy,

Relaterade artiklar