Reputation: 1677
I'm doing a web application in Angular v11.0.2. I need to do a HTTP call to show some information on screen. To save time and to take advantage of the async
pipe benefits, I'm using it in the template, but, it's causing multiple requests that never stops.
Service
export class MyDataService {
constructor(
private http: HttpClient,
) {
}
fetchMyEmployeeData(): Observable<any[]> {
return this.http.post<any[]>('ENDPOINT', {});
}
}
Component
export class MyAwesomeComponent {
constructor(
public myDataService: MyDataService,
) {
}
}
Template
<ng-container *ngIf="(myDataService.fetchMyEmployeeData() | async) as data">
</ng-container>
This causes multiple requests and never stops.
The same happens if I use *ngFor
:
<tr *ngFor="let d of (myDataService.fetchMyEmployeeData() | async)">
<td>.</td>
</tr>
I have tried the following this:
Using the shareReplay
operator:
fetchMyEmployeeData(): Observable<any[]> {
return this.http.post<any[]>('ENDPOINT', {}).pipe(shareReplay());
}
Using a simple div:
<div *ngIf="(myDataService.fetchMyEmployeeData() | async) as data">
</div>
I know that if I subscribe from the component and save the results in a local variable, I can call it in the template, but this is not my goal. Working example:
export class MyAwesomeComponent implements OnInit {
data: any[];
constructor(
public myDataService: MyDataService
) {
}
ngOnInit() {
// This is not my goal because I will need to handle the subscription life manually.
this.myDataService.fetchMyEmployeeData().subscribe(res => this.data = res);
}
}
I have also followed the recommendations given here:
I don't know exactly what causes this multiple requests and how can I avoid it.
My goal is to use the async
pipe in the template and do just one HTTP call.
Upvotes: 13
Views: 5867
Reputation: 36
Calling service method fetchMyEmployeeData() will always return NEW observable.
So if you will create a component variable and assign to it result of fetchMyEmployeeData() it will have only 1 unique observable. And subscribing to it via async pipe will not trigger endless http requests.
serverData = myDataService.fetchMyEmployeeData();
<ng-container *ngIf="serverData | async">
</ng-container>
Upvotes: 1
Reputation: 35
To elaborate a little bit on why you need initialize it as a property of your component. What was happening is when Angular's change detection runs it sees the expression myDataService.fetchMyEmployeeData()
and executes it. This returns you a new observable and makes the request. Change detection keeps on running so it will be called multiple times.
IMHO the template should be as dumb as possible, as in it displays the data you give it and does not get data itself.
Upvotes: 0
Reputation: 577
UPDATE: shareReplay is needed if you call variable multiple times in template, misunderstand the question.
shareReplay()
should be on component level, cause when you call the fetchMyEmployeeData()
it is a function that returns new Observables(and new shareReplays for each)
in component create:
employeeData$ = this.myDataService.fetchMyEmployeeData().pipe(shareReplay());
and use in template as
employeeData$ | async
in this case, it would do only a single request for back-end
Upvotes: 11