That makes perfect sense. This is how Angular performs change detection. And this is Angular performing extra checks since you called a function in one of the data-binding syntaxes, here:
[ngStyle]="{'background-color': getBG(row*col)}"
Angular performs Change Detection in three cases:
- DOM Events.
- AJAX Calls.
- Timeouts / Intervals.
This is the case of DOM Events (click
).
Now when performing Change Detection, Angular check whether a particular variable in the Component has changed.
That's pretty straight forward in case of properties. But not so straight-forward in case of functions.
You see, the only way to determine whether the value of a function has changed is by calling it.
So Angular is doing just that.
SOLUTION:
Just create a matrix for the number to show and the color to paint right in the Component Class:
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
rows = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
cols = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
matrix = [];
model1 = '';
model2 = '';
model3 = '';
model4 = '';
model5 = '';
ngOnInit() {
this.rows.forEach((row, rowIndex) => {
this.matrix.push([]);
this.cols.forEach((col, colIndex) => {
const product = row * col;
this.matrix[row].push({
numberToShow: product,
color: this.getBG(product),
});
})
});
}
getBG(hue: number): string {
console.log('getBG was called');
return 'hsl(' + hue + ', 100%, 50%)';
}
}
And then use it in your template:
<br/>
<div> 1. Open a console</div>
<br/>
<section>
<div class="row" *ngFor="let row of matrix">
<div
class="col"
[style.background-color]="col.color"
*ngFor="let col of row ">
{{col.numberToShow}}
</div>
</div>
</section>
<br/>
<div>2. Click fast on the different inputs: </div>
<br/>
<section>
<input type="text" [ngModel]="model1"/>
<input type="text"[ngModel]="model2"/>
<input type="text"[ngModel]="model3"/>
<input type="text"[ngModel]="model4"/>
<input type="text"[ngModel]="model5"/>
</section>
Difference in the performance:
In the previous implementation, the getBG
was called 401 times on initialization.
In the solution implementation, the getBG
is called 101 times on initialization.
That's a massive performance gain of around 397%.
Plus there's no extra call to the getBG
method when the user focuses and blurs out from any input field.
Here's a Working Sample StackBlitz for your ref.
You might also want to read through a Medium Article that I wrote about Performant Reactive Forms in Angular. Although it's related to Reactive Forms, I've touched upon this aspect, in the article. I'm sure you'll find it helpful.