how to use track by inside ngFor angular 2 how to use track by inside ngFor angular 2 angular angular

how to use track by inside ngFor angular 2


As pointed out in @Eric comment, and after lots of reading and playing around, here is how to use trackBy in angular2

  1. the first thing you need to know its not same syntax as angular1, now you need to separate it from the for loop with a ;.

Usage 1: Track by property of object

 // starting v2. 1 this will throw error, you can only use functions in trackBy from now on<ion-card *ngFor="let post of posts;trackBy:post?.id"></ion-card> // **DEPRECATED**---or---<ion-card *ngFor="let post of posts;trackBy:trackByFn"></ion-card>

here you ask angular2 to

  1. create a local variable post;
  2. you tell trackBy to wait untill this local variable is ready "you do that by using elvis operator 'the question mark after thevariable name', then use its id as tracker.

so

// starting v2. 1 this will throw error, you can only use functions in trackBy from now on*ngFor="#post of posts;trackBy:post?.id"

is what same as angular's 1

ng-repeat="post in posts track by post.id"

Usage 2: Track using your own Function

@Page({    template: `        <ul>            <li *ngFor="#post of posts;trackBy:identify">              {{post.data}}            </li>        </ul>    `})export class HomeworkAddStudentsPage {    posts:Array<{id:number,data:string}>;       constructor() {        this.posts = [  {id:1,data:'post with id 1'},                        {id:2,data:'post with id 2'} ];    }    identify(index,item){      //do what ever logic you need to come up with the unique identifier of your item in loop, I will just return the object id.      return post.id      }}

trackBy can take a name of callback, and it will call it for us supplying 2 parameters: the index of the loop and the current item.

To achieve the same with Angular 1, I used to do:

<li ng-repeat="post in posts track by identify($index,post)"></li>app.controller(function($scope){  $scope.identify = function(index, item) {return item.id};});


As you already recognized, using a function is the only way to use trackBy in Angular 2

<ion-card *ngFor="#post of posts;trackBy:identify"></ion-card>

The official documentation states that https://angular.io/docs/ts/latest/api/common/index/NgFor-directive.html

All the other information about <ion-card *ngFor="let post of posts;trackBy:post?.id"></ion-card> is wrong. Starting with Angular 2.4.1 this will also throw an error in the application.


Just want to add few examples (Angular 2+) in addition to others' answer to make the use of trackBy clear.

From documentation:

To avoid this expensive operation, you can customize the default tracking algorithm. by supplying the trackBy option to NgForOf. trackBy takes a function that has two arguments: index and item. If trackBy is given, Angular tracks changes by the return value of the function.

Read more here: https://angular.io/api/common/NgForOf

An example will explain it better.

app.component.ts

   array = [      { "id": 1, "name": "bill" },      { "id": 2, "name": "bob" },      { "id": 3, "name": "billy" }   ]   foo() {      this.array = [         { "id": 1, "name": "foo" },         { "id": 2, "name": "bob" },         { "id": 3, "name": "billy" }      ]   }   identify(index, item) {      return item.id;   }

Let's display the array into 3 div using *ngFor.

app.component.html

Example of *ngFor without trackBy:

<div *ngFor="let e of array;">   {{e.id}} - {{e.name}}</div><button (click)="foo()">foo</button>

What happend if we click on foo button ?

→ The 3 divs will be refreshed. Try it yourself, open your console to verify.

Example of *ngFor with trackBy:

<div *ngFor="let e of array; trackBy: identify">   {{e.id}} - {{e.name}}</div><button (click)="foo()">foo</button>

What happend if we click on foo button ?

→ Only the first div will be refreshed. Try it yourself, open your console to verify.

And what if we updated the first object instead of the whole object ?

   foo() {      this.array[0].name = "foo";   }

→ There is no need to use trackBy here.

It's especially usefull when using Subscription which often looks like what I schematized with array. So it would looks like:

   array = [];   subscription: Subscription;   ngOnInit(): void {      this.subscription = this.fooService.getArray().subscribe(data => {         this.array = data;      });   }   identify(index, item) {      return item.id;   }