Pretty standard situation.
There is one parent component <item-list>
.
Inside its template with *ngFor
generated 20 child components <item-block>
.
Child component styles set with [ngStyle]
directive and template expression that calls function setStyles()
.
The problem (or maybe not) is that when any event emitted on one specific child element, expression setStyles()
executed twice for each of child components.
So if we click on one specific item in our example, and we have 20 <item-block>
components - setStyles()
will be executed 20+20 times.
The questions are:
- Why its happening and is it expected behavior.
- How it affect performance
- How it could be avoided - only one call per child component/detection change.
Example & plnkr:
plnkr (click on item - open console for debug output)
import {Component} from '@angular/core'
@Component({
selector: 'item-list',
template: `
<item-block
[item]="item"
*ngFor="let item of items"
></item-block>
`,
})
export class ItemListComponent {
items: any[] = [];
constructor() {}
ngOnInit() {
// generate dummy empty items
for (let i = 0; i < 20; i++) {
this.items.push(
{
value: 'item #' + i;
}
)
}
}
}
import {Component, Input} from '@angular/core'
@Component({
selector: 'item-block',
template: `
<div
class="item"
[ngStyle]="setStyles()"
(click)="testClick($event)"
>{{item.value}}</div>
`,
})
export class ItemBlockComponent {
@Input() item: any;
constructor() {}
testClick(): void{
console.log('item clicked');
}
setStyles(){
console.log('seting styles...');
return {
'background': '#ccc'
};
}
}
causes
setStyles
to be called every time change detection is run (which can be quite often and will hurt performance). Also becausesetStyles()
returns a different object instance every time, it should cause an exception. "Expression changed since it was last checked" or similar.Calling methods from the view this way is discouraged.
Instead assign the value to a property and bind to that property: