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

断了今生、忘了曾经 提交于 2020-06-27 10:13:33

问题


I have the following component Template:

<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() is a simple method returning an Observable<Date>:

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

My goal would be to invoke the method and subscribe to the observable with the async pipe in the template. However when I run the application endless GET and OPTIONS requests are generated.

Also if I place the method call outside the ngFor the same happen. The call would need to be executed inside the ngFor as the parameter is different for each collection item.

Why the method is simply called once and no more calls generated after the subscription?


回答1:


Calling functions in template is usually not a very good idea as it leads to unpredictable results. This is how you can restructure your code to avoid this:

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}`);
}

And in your template:

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

Here's a stackblitz I created where you can see how a similar mechanism works: https://stackblitz.com/edit/angular-nyn4qz




回答2:


Angular calls getNext every event cycle, and each time getNext makes new http request and returns new Observable. You need to cache Observable from first function call. I recommend you to create them somewhere in controller, and then pass in template as variables.




回答3:


Most certainly your problem is related to change detection.

Everytime angular considers there can be changes to what is necessary to draw your template (i.e. anytime there is a browser event except if your component is OnPush), it will redraw the component, and thus retrigger the loop and the observable.

In that case you have two choices:

  • ensure change detection is not triggered when not needed (for example by making your component follow the OnPush ChangeDetectionStrategy) but it mostly work only if there is a limited set of @Input() that triggers the update of the component.
  • do the requests only once in ngOnInit or in ngOnChanges (in the case data is an @Input() of your component) and store the results in an array that you base your template on to do the for loop (I would go this way).


来源:https://stackoverflow.com/questions/48813002/async-pipe-in-template-inside-ngfor-block-triggers-http-get-calls-loop

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!