Reputation: 184
I'm facing an issue with my current angular setup and I can't figure out what exactly is wrong. I will first describe some context before I go to the problem.
I have a class - data-service.ts, which has a method "getData" inside. This is the body:
getData(): Observable<Data[]> {
let params = new HttpParams();
params = params.append('user_id', this.userId);
return this.http.get<Data[]>('api/data', {
observe: 'body',
responseType: 'json',
params
}).pipe(
map((response: any[]) => {
return response.map((s) => ({
id: s.id,
userId: s.user_id,
type: s.type_id,
...
}));
})
);}
My requirement is to fetch this data and refresh the view every time user enters a targeted tab in ngModal. Modal component "modal-component.ts" fetches this data when you click that targeted tab. Clicking on the tab fires an event, by this piece of code:
getData(evt) {
this.data$ = this.dataSevice.getData();
}
I'm passing this "data$" observable to child component using async pipe:
<modal-component>
...
<app-data-manager [data]="data$ | async"></app-data-manager>
...
</modal-component>
Inside of "data-manager" I'm using *ngFor on [data], and for every data I'm rendering some html. In that HTML I'm using *ngIf to determine which template, first or second, should be rendered.
<div *ngIf="isTrue(dataEntry); then first else second"></div>
Problem is: isTrue method is called for every dataEntry, which is fine, but it's getting called a multiple times for entire data set. Like hundreads of times. I tried to using promises, using Take(1) pipe, assigning boolean (the one that ngIf uses to choose template) during mapping, assigning data inside of a subscribe and passing normal collection instead of using async pipe. I don't know that is causing this - I appreciate every help on that one. Thank you!
Additional info When I use dataEntry.isTrue instead of isTrue(dataEntry) in *ngIf - I'm still getting same results. I also added "OnChanges" in child component with console log inside - on first click it logs to console once, on second click it logs to console twice. Two same messages appear.
Upvotes: 2
Views: 2821
Reputation: 2379
The problem is you are using a function call in an angular expression. Don't do this.
[T]he
isTrue()
function is executed every time Angular change detection runs. And that can be many times!
Because Angular cannot predict whether the return value of
isTrue()
has changed, it needs to execute the function every time change detection runs.
This means that any change detection throughout the app (including outside of app-data-manager
component) will cause isTrue()
to execute.
This article talks about it more: https://medium.com/showpad-engineering/why-you-should-never-use-function-calls-in-angular-template-expressions-e1a50f9c0496
One solution is to use properties instead of a function. If you want to wrap up complex logic, opt for making a custom pipe like so.
*ngIf="(dataEntry | isTruePipe); then first else second"></div>
Upvotes: 5