Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular2 template expression called twice for each component on change-detection

Tags:

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:

  1. Why its happening and is it expected behavior.
  2. How it affect performance
  3. 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'
      };
  }
}
like image 636
Alex Kalmikov Avatar asked Jan 04 '17 18:01

Alex Kalmikov


2 Answers

[ngStyle]="setStyles()"

causes setStyles to be called every time change detection is run (which can be quite often and will hurt performance). Also because setStyles() 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:

[ngStyle]="myStyles"
like image 101
Günter Zöchbauer Avatar answered Sep 24 '22 10:09

Günter Zöchbauer


In default (development mode) Angular run Detect Changes mechanism twice.
In production mode it is reduce to single change.

How to switch Detect Changes mechanism to production?

In main.ts file try to add:

import { enableProdMode } from '@angular/core';
// ...
enableProdMode();
// ...
platformBrowserDynamic().bootstrapModule(AppModule)

and reload application.

like image 20
piecioshka Avatar answered Sep 23 '22 10:09

piecioshka