58

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 ?

Sujatha Girijala
  • 1,141
  • 8
  • 20
Zalaboza
  • 8,899
  • 16
  • 77
  • 142
  • Can you please update me what is the use of trackBy in ngFor? Eric's answer seems to be working fine. – micronyks Mar 31 '16 at 03:13
  • @micronyks It allows you to pass a function to `*ngFor` that `NgFor` forwards to `https://angular.io/docs/ts/latest/api/core/IterableDifferFactory-interface.html` https://github.com/angular/angular/blob/master/modules/angular2/src/common/directives/ng_for.ts#L82 (don't know how that diffing is working exactly yet though) – Günter Zöchbauer Mar 31 '16 at 05:19
  • `https://angular.io/docs/ts/latest/api/core/IterableDifferFactory-interface.html‌`​ not found. Any other link? – micronyks Mar 31 '16 at 05:37
  • 2
    Still I'm not clear with clean usage of `trackBy`. – micronyks Mar 31 '16 at 05:40
  • i would be very helpful if anyone here post example with the usage of trackBy in `*ngFor` – Pardeep Jain Mar 31 '16 at 05:53
  • okay got it thanks @Zalaboza – Pardeep Jain Mar 31 '16 at 10:54

5 Answers5

96

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};
});
isherwood
  • 58,414
  • 16
  • 114
  • 157
Zalaboza
  • 8,899
  • 16
  • 77
  • 142
  • What if the `item` is an immatable.js map, and to access the `id`, you need to do `item.get('id')`? – Daniel Birowsky Popeski Jun 30 '16 at 11:30
  • 5
    I do not think that "trackBy:post?.id" works. In my case, the state of the child element is lost. A separate trackBy function works though (but is definitely not convenient). By the way, the elvis operator itself indicates that something is wrong. If it worked, then you would not have to use it here (unless the collection items can be null). – Joel Jul 06 '16 at 13:37
  • I can only second what @JoelRichard said: Specifying a field of the traversed item doesn't work for me either. The official docs also don't mention this shortcut syntax: https://angular.io/docs/ts/latest/api/common/index/NgFor-directive.html. – Marcus Riemer Jul 24 '16 at 15:40
  • @Zalaboza It seems the syntax is outdated and should be
  • ...
  • can you update it, please? – ezain Sep 21 '16 at 10:11
  • @Birowsky for immutables u have to use functions and return item.get('id') – Zalaboza Sep 21 '16 at 10:48
  • 19
    I confirm, trackBy only works with functions, it doesn't error with "post?.id" but it will trackBy "null" in this case (you can inspect the html and see that), which means that it doesn't track at all, you just don't see an error @Zalaboza could you update your post so that it is not misleading for new users ? – Olivier Nov 25 '16 at 14:45
  • 2
    In addition to @Olivier comment. Starting with Angular 2.4.1, using *ngFor="#post of posts;trackBy:post?.id" will thrown an error, because trackBy only accepts functions – Volker Andres Dec 22 '16 at 12:22
  • 3
    Right @Zalaboza, only function is allowed, please update your post. Discussed here: https://github.com/angular/angular/issues/13641#issuecomment-269082370 – Nico Toub Dec 24 '16 at 16:35
  • 1
    @Zalaboza Would you please update your answer which `trackBy:post?.id` has been confirmed not working. – Will Huang Aug 09 '17 at 18:02