Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterating over *ngFor with a static array of objects constantly updates the DOM

I have a simple component which iterates over a static array of objects and displays the content. When this component is added into the larger application, the content is displayed correctly but I am unable to register any click events on it. When I inspect the element in chrome using right click -> Inspect I am greeted with the body tag and it doesn't drill down to the individual element as expected.

By navigating manually, inspecting the element idicates that the component is constantly being updated by the DOM (div tag flashes). By my understanding there shouldn't be any change detected since it is a static array.

Changing the object array to a simple string array ['a', 'b', 'c'] behaves as expected and the DOM isn't being updated.

Adding additional elements the template outside of the ngFor are unaffected and aren't being constantly updated.

I am using v2.4.1

Simplified Component

import { Component } from '@angular/core';

@Component({
	selector: 'app-ngfor-test',
	templateUrl: './ngfor-test.component.html',
	styleUrls: ['./ngfor-test.component.css']
})
export class NgforTestComponent {
	
	get items() {
		return [
			{
				'label': 'a',
				'value': 'first'
			},
			{
				'label': 'b',
				'value': 'second'
			},
			{
				'label': 'c',
				'value': 'third'
			}
		];
	}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

Template

<div class="item-container" *ngFor="let item of items">
	<label>{{ item.label | uppercase }}</label>
	<span>{{ item.value }}</span>
</div>
like image 561
Christopher Moore Avatar asked Jan 12 '17 11:01

Christopher Moore


2 Answers

As @seidme mentioned the getter is called every time change detection runs, which can be quite often.

get items() {
    return [
        {
            'label': 'a',
            'value': 'first'
        },
        {
            'label': 'b',
            'value': 'second'
        },
        {
            'label': 'c',
            'value': 'third'
        }
    ];
}

returns a new object instance every time it is called, which causes ngFor to re-render the list.

If you change it to

items = [
        {
            'label': 'a',
            'value': 'first'
        },
        {
            'label': 'b',
            'value': 'second'
        },
        {
            'label': 'c',
            'value': 'third'
        }
    ];

get items() {
  return this.items;
}

it will stop re-rendering, because Angular recognizes it got the same object instance and nothing has changed and nothing needs to be updated or re-rendered.

like image 113
Günter Zöchbauer Avatar answered Oct 10 '22 00:10

Günter Zöchbauer


You're passing a getter to the *ngFor directive. get syntax binds an object property to a function that will be called when that property is looked up (every time Angular runs change detection), which in your case always returns a new array object (reference changes), forcing the Angular to re-render the list.

like image 42
seidme Avatar answered Oct 09 '22 23:10

seidme