The Cost of Calling Functions from HTML in Angular - Best Practices

Calling a function directly from the HTML file in Angular can be costly because it involves evaluating the expression for every change detection cycle. Change detection is a process in Angular that checks for changes in the component's properties, input properties, or events, and updates the view accordingly.

When you call a function in the HTML template, Angular has to evaluate that function on every change detection cycle to determine if any changes have occurred that require a view update. This can lead to performance issues if the function is complex or if it is called frequently.

Imagine that we have a super expensive function, called `sumAllNumbersFromZeroToAMillion()` which would sum all numbers from 0 to a million with the following code:

typescript
@Component({
  selector: 'app-my',
  templateUrl: './my.component.html',
  styleUrls: ['./my.component.scss'],
})
export class MyComponent {
  sumAllNumbersFromZeroToAMillion() {
    console.log('PERFORMING EXPENSIVE OPERATION');
    let sum = 0;
    for (let i = 0; i <= 1000000; i++) {
      sum += i;
    }
    return sum;
  }
}

Calling the expensive function from the HTML would trigger the calculations for every single change detection cycle, which is unnecessary and a bad practise in general. Here's an example to illustrate the cost of calling a function from an HTML template in Angular:

html
<h1>A super expensive operation</h1>

{{ sumAllNumbersFromZeroToAMillion() }}

In this example, the `sumAllNumbersFromZeroToAMillion()` is called every time the view is updated. If this function is computationally expensive or performs a lot of I/O operations, it can significantly impact the application's performance.

To avoid this issue, it's best to avoid calling functions directly from the HTML template and instead, use property bindings to display data. For example:

html
<h1>A super expensive operation</h1>

{{ sum }}

But this would require you to change the way the code works by introducing a variable `sum`, and calling it in the `ngOnInit()`-life cycle hook. For example:

typescript
@Component({
  selector: 'app-my',
  templateUrl: './my.component.html',
  styleUrls: ['./my.component.scss'],
})
export class MyComponent implements OnInit {
  sum = 0;

  ngOnInit(): void {
    this.sum = this.sumAllNumbersFromZeroToAMillion();
  }

  sumAllNumbersFromZeroToAMillion() {
    console.log('PERFORMING EXPENSIVE OPERATION');
    let sum = 0;
    for (let i = 0; i <= 1000000; i++) {
      sum += i;
    }
    return sum;
  }
}

In this example, the `sum` property is bound to the HTML template, and Angular updates the view automatically when the value of data changes.