如何在 ngFor angular 2 中使用 track by

how to use track by inside ngFor angular 2

尝试了我能猜到的所有语法都无法正常工作!

<!--- 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>

唯一对我有用的方法是

  1. 控制器中的创建方法Class

    识别(索引,post:Post){ return post.id }

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

这是唯一的方法吗?我不能只为 trackBy 指定一个内联的 属性 名称吗?

正如@Eric 评论中指出的那样,经过大量阅读和尝试,这里是如何在 angular2

中使用 trackBy
  1. 首先您需要了解它与 angular1 不同的语法,现在您需要使用 ;.
  2. 将它与 for 循环分开

用法 1:通过对象 属性 跟踪

 // 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>

这里你问angular2到

  1. 创建局部变量post;
  2. 你告诉 trackBy 等到这个局部变量准备好“你通过使用 elvis 运算符来做到这一点” 变量名',然后使用它的 id 作为跟踪器。

所以

// starting v2. 1 this will throw error, you can only use functions in trackBy from now on

*ngFor="#post of posts;trackBy:post?.id"

与angular的1

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

用法 2:使用您自己的函数进行跟踪

@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 可以取一个回调的名字,它会为我们调用它提供 2 个参数:循环的索引和当前项目。

为了实现与Angular 1相同,我曾经这样做:

<li ng-repeat="post in posts track by identify($index,post)"></li>

app.controller(function($scope){
  $scope.identify = function(index, item) {return item.id};
});

如您所知,使用函数是在 Angular 2

中使用 trackBy 的唯一方法
<ion-card *ngFor="#post of posts;trackBy:identify"></ion-card>

官方文档指出 https://angular.io/docs/ts/latest/api/common/index/NgFor-directive.html

关于 <ion-card *ngFor="let post of posts;trackBy:post?.id"></ion-card> 的所有其他信息都是错误的。从 Angular 2.4.1 开始,这也会在应用程序中引发错误。

trackBy 背后的概念:

  1. ngFor of angular 通过跟踪对象标识自动优化 modified/created/deleted 对象的显示。 因此,如果您在列表中创建所有新对象然后使用 ngFor,它将呈现整个列表。

  2. 让我们考虑一个场景,尽管进行了所有 ngFor 优化,渲染仍然需要时间。在这种情况下,我们使用 trackBy。因此,我们可以提供另一个参数来跟踪对象而不是默认跟踪标准的对象标识。

一个运行例子:

<!DOCTYPE html>
<html>

<head>
    <title>Angular 2.1.2 + TypeScript Starter Kit</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <script src="https://unpkg.com/zone.js@0.6.21/dist/zone.js"></script>
    <script src="https://unpkg.com/reflect-metadata@0.1.9/Reflect.js"></script>
    <script src="https://unpkg.com/systemjs@0.19.41/dist/system.js"></script>
    <script src="https://unpkg.com/typescript@2.1.4/lib/typescript.js"></script>
    <script src="config.js"></script>
  <script>
      System.import('app')
          .catch(console.error.bind(console));
    </script>
</head>

<body>
    <my-app>
        loading...
    </my-app>
</body>

</html>

除了其他人的回答之外,我只想添加几个示例(Angular 2+),以使 trackBy 的使用更加清晰。

来自文档:

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.

在此处阅读更多内容:https://angular.io/api/common/NgForOf

一个例子会更好地解释它。

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;
   }

让我们使用 *ngFor.

array 显示为 3 div

app.component.html

*ngFor 没有 trackBy 的例子:

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

如果我们点击 foo 按钮会发生什么?

→ 3 div会被刷新。 亲自尝试,打开控制台进行验证。

*ngForwith trackBy的示例:

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

如果我们点击 foo 按钮会发生什么?

→ 只会刷新第一个div。 亲自尝试,打开控制台进行验证。

如果我们更新第一个对象而不是整个对象呢?

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

→这里不用trackBy

它在使用 订阅 时特别有用,它通常看起来像我用 array 概括的内容。所以它看起来像:

   array = [];
   subscription: Subscription;

   ngOnInit(): void {
      this.subscription = this.fooService.getArray().subscribe(data => {
         this.array = data;
      });
   }

   identify(index, item) {
      return item.id;
   }

这是 Angular 具有严格类型检查的全局 trackBy 属性 指令,通过传递 属性:

完全在模板中处理 trackBy
import { NgForOf } from '@angular/common';
import { Directive, Host, Input, NgIterable } from '@angular/core';

@Directive({
    selector: '[ngForTrackByProperty]'
})
export class NgForTrackByPropertyDirective<T> {

    @Input() ngForOf!: NgIterable<T>;
    @Input() ngForTrackByProperty!: keyof T;

    constructor(@Host() ngForOfDir: NgForOf<T>) {
        ngForOfDir.ngForTrackBy = (_, item: T): T[keyof T] => item[this.ngForTrackByProperty];
    }
}

用法

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

interface Item { 
  id: number; 
  name: string;
}

@Component({
  selector: 'app-root',
  template: `
    <ul>
      <li *ngFor="let item of list; trackByProperty: 'id'">
        {{ item.id }} {{ item.name }}
      </li>
    </ul>
  `,
})
export class AppListComponent {
  public list: Array<Item> = [
    { id: 0, name: 'foo' },
    { id: 1, name: 'bar' },
    { id: 2, name: 'baz' },
  ];
}

NPM https://www.npmjs.com/package/ng-for-track-by-property

github https://github.com/nigrosimone/ng-for-track-by-property