
One of the most interesting features of the front-end libraries is the ability to maintain in binding the user’s interface and properties, which contains data to be displayed. Angular also offers this feature: it offers a mechanism called Change Detection, thanks to which it “understands” when to update the interface if an application’s change occurs.
On the web, you can find many articles about the detailed mode of operation of this mechanism and how it uses zone.js to understand when to intervene to update the UI, but I would like to highlight, with an easy example, how this mechanism can impact on our application’s performances, if we are not careful enough.
We start from a simple component, in which we insert a text box with a bidirectional binding with a property of the component itself. When we digit text in this box and then press “enter”, we would like to push the typed string within a simple not ordered list, together with a number between 1 and 10, casually produced. If we want to see, to the naked eye, what is the impact that the change detection might have on the user experience, we have to force a little bit: when we display the element of the list, we don’t restrict ourselves to print the numeric value, but we invoke a method directly from binding expression, which will calculate from the original value the corresponding Fibonacci number, multiplied by 4:
import { Component } from '@angular/core';
@Component({
selector: 'app-sample-list',
templateUrl: './sample-list.component.html'
})
export class SampleListComponent {
text: string;
items: {label: string, value: number}[] = [];
addTextToList() {
const randomValue = Math.floor(Math.random() * 10) + 1;
this.items.push({ label: this.text, value: randomValue });
}
generateValue(value: number) {
console.log('value ' + value + ' generated');
return this.fibonacci(value * 4);
}
private fibonacci(num: number): number {
if (num === 1 || num === 2) {
return 1;
}
return this.fibonacci(num - 1) + this.fibonacci(num - 2);
}
}
You might remind from your IT course, that the calculation of the value of the Fibonacci sequence has a computational complexity, that increases exponentially as the value itself increases. This implementation is, obviously, only an example, but it is useful for us to exaggerate the problem. Note that, in the code, I don’t call the algorithm directly, but I invoke a method, which prints a string in the console first, then runs the calculation. This system allows us to see how many times the method has been called while we interact with the interface:

As you can see in the image, as you modify the text, the change detection call back the calculation’s method for every element in the list. If you try to insert some elements, you realize that the application’s responsivity visibly decreases after a short time.
How can we intervene to improve the situation? A first approach can be the reorganization of our component and externalization of the text box, so that the list to be displayed would not be processed at the same time, but will be passed by input to the component. In our example, we move the text and the list management in app-component:
<input type="text" [(ngModel)]="text" (keydown.enter)="addTextToList()">
<app-sample-list [items]="items"></app-sample-list>
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
text: string;
items: {label: string, value: number}[] = [];
addTextToList() {
const randomValue = Math.floor(Math.random() * 10) + 1;
this.items.push({ label: this.text, value: randomValue });
}
}
We leave in the list component only the part of the calculation of the Fibonacci sequence. In Angular, the change detection spreads from the component we are operating with, to all its sons: we don’t solve the problem if we move the text box in the father-component. We can though disable the change detection in the list component, in order to avoid the value to be recalculated any time: you just need to set the component’s set detection strategy in its designer. Values, which are possible, are: Default, default behavior and OnePush. These values allow us to tell Angular, that we will decide when to run the interface update. It is interesting to note that the change detection interruption does not apply to input parameters, then, if the input value changes, the interface will update:
import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
@Component({
selector: 'app-sample-list',
templateUrl: './sample-list.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SampleListComponent {
@Input()
items: {label: string, value: number}[] = [];
generateValue(value: number) {
console.log('value ' + value + ' generated');
return this.fibonacci(value * 4);
}
private fibonacci(num: number): number {
if (num === 1 || num === 2) {
return 1;
}
return this.fibonacci(num - 1) + this.fibonacci(num - 2);
}
}
As you run this code, anyway, the application stops working. This happens because we are providing in input an array, that grows up with every input, but the connection to the array doesn’t change and Angular doesn’t notice the change. The immutability comes to help us to solve the problem. As first approach, we try to create the array again, any time we add a value: the easiest way to do that is to convert the array in a string and then transform it again in an array:
addTextToList() {
const randomValue = Math.floor(Math.random() * 10) + 1;
this.items.push({ label: this.text, value: randomValue });
this.items = JSON.parse(JSON.stringify(this.items));
}
It’s nasty, but effective. With this solution, you can see that the modification to the text in the box doesn’t cause the recalculation of values in the list:

This approach has the problem that the operation to recreate the list at every input is both uncomfortable and not very efficient whether considering the time or the memory allocation. To solve this further problem, we can use immutable.js (https://immutable-js.github.io/immutable-js/), which provides all necessary instruments to work with immutable objects, optimizing the resources. You can easily install it with npm i immutable. At this stage, we can use immutable lists, instead of the simple array, that provides an unshift method to add a new element, which returns a new array’s instance.
import { Component } from '@angular/core';
import { List } from 'immutable';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
text: string;
items = List<{label: string, value: number}>();
addTextToList() {
const randomValue = Math.floor(Math.random() * 10) + 1;
this.items = this.items.unshift({ label: this.text, value: randomValue });
}
}
This library optimizes the resources with the creation of a data structure, where the elements already present will not be lost, but used again in the new array.
Now we will use a little-known feature of Angular’s Pipes. We create a Pipe to calculate the Fibonacci sequence, instead of invoking the component method:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'fibonacci',
pure: true
})
export class FibonacciPipe implements PipeTransform {
transform(value: any, ...args: any[]): any {
console.log('value ' + value + ' generated');
return this.fibonacci(value * 4);
}
private fibonacci(num: number): number {
if (num === 1 || num === 2) {
return 1;
}
return this.fibonacci(num - 1) + this.fibonacci(num - 2);
}
}
In the code, I make explicit the Pipe’s pure parameter default value: this value is “true”. The meaning is that, even if it’s not clearly declared, as in this case, Pipes are treated as pure functions, thus the result of their elaboration will change only if parameters change. A pure Pipe in Angular would not be revoked if parameters don’t change since the result would not change too. This feature optimizes our code very much, because we will invoke the Pipe only on newly inserted values:

if you set the parameter pure to false, you will discover that Angular would invoke the Pipe every time. Even if we leave the Pipe pure, we cannot avoid the value to be calculated for any new line, although parameters could correspond to a different line. As a matter of fact, the n-simo value of the Fibonacci sequence It’s always the same, thus, once calculated, we could reuse it every time the same value is required. Angular has no integrated instrument for this functionality, which is only a values’ caching strategy. However, we can install a library, that can do that with the command npm i memo-decorator. . Pipe becomes:
import { Pipe, PipeTransform } from '@angular/core';
import memo from 'memo-decorator';
@Pipe({
name: 'fibonacci'
})
export class FibonacciPipe implements PipeTransform {
@memo()
transform(value: any, ...args: any[]): any {
console.log('value ' + value + ' generated');
return this.fibonacci(value * 4);
}
private fibonacci(num: number): number {
if (num === 1 || num === 2) {
return 1;
}
return this.fibonacci(num - 1) + this.fibonacci(num - 2);
}
}
with the addition of the memo decorator to the transform method, we save the result of the elaboration the parameters being equal, in order to skip the execution for already calculated values:

You find the code here:
https://github.com/apomic80/angular-performance-improvements
Happy Coding!