问题
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 inngOnChanges
(in the casedata
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