how to use track by inside ngFor angular 2

Zalaboza picture Zalaboza · Mar 31, 2016 · Viewed 92.2k times · Source

tried every syntax i can guess couldnt make it works !

<!--- THIS WORKS FINE --->
<ion-card *ngFor="#post of posts">
{{post|json}}
</ion-card>

<!--- BLANK PAGE --->
<ion-card *ngFor="#post of posts track by post.id">
{{post|json}}
</ion-card>

<!--- Exception : Cannot read property 'id' of undefined --->
<ion-card *ngFor="#post of posts;trackBy:post.id">
{{post|json}}
</ion-card>

<!--- Exception : Cannot read property 'undefined' of undefined --->
<ion-card *ngFor="#post of posts;trackBy:posts[index].id">
{{post|json}}
</ion-card>

<!--- Blank page no exception raised !  --->
<ion-card *ngFor="#post of posts;#index index;trackBy:posts[index].id">
{{post|json}}
</ion-card>

the only approach that worked for me was

  1. Creating method in controller Class

    identify(index,post:Post){ return post.id }

and

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

is this is only way ? can i not just specify a property name inline for trackBy ?

Answer

Zalaboza picture Zalaboza · Mar 31, 2016

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 the variable 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};
});