如何在ngFor中使用trackBy

时间:2017-02-08 08:35:58

标签: angular ngfor angularjs-track-by

我无法理解我应该从trackBy返回什么。根据我在网上看到的例子,我应该返回对象上某些属性的值。这是对的吗?为什么我将索引作为参数?

例如,在以下情况中:

constructor() {
    window.setInterval(() => this.users = [
            {name: 'user1', score: Math.random()},
            {name: 'user2', score: Math.random()}],
        1000);
}

userByName(index, user) {
    return user.name;
}

...

<div *ngFor="let user of users; trackBy:userByName">{{user.name}} -> {{user.score}}</div>

尽管名称未更改,但模板中显示的对象仍会更新。为什么呢?

5 个答案:

答案 0 :(得分:26)

ngDoCheck指令触发的每个ngForOf上,Angular会检查哪些对象已更改。它对此过程使用不同,每个都使用trackBy函数来比较当前对象和新对象。默认的trackBy函数按标识跟踪项目:

const trackByIdentity = (index: number, item: any) => item;

它接收当前项目并应返回一些值。然后将函数返回的值与此函数上次返回的值进行比较。如果值更改,则差异会报告更改。因此,如果默认函数返回对象引用,则在对象引用已更改时,它将与当前项不匹配。因此,您可以提供自定义trackBy函数,该函数将返回其他内容。例如,对象的一些键值。如果此键值与前一个键值匹配,则Angular将不会检测到更改。

不再支持语法...trackBy:userByName。您现在必须提供功能参考。这是基本的例子:

setInterval( () => {
  this.list.length = 0;
  this.list.push({name: 'Gustavo'});
  this.list.push({name: 'Costa'});
}, 2000);

@Component({
  selector: 'my-app',
  template: `
   <li *ngFor="let item of list; trackBy:identify">{{item.name}}</li>
  `
})
export class App {
  list:[];

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

虽然对象引用发生了更改,但DOM不会更新。 Here is掠夺者。如果您对ngFor的工作原理感到好奇,read this answer

答案 1 :(得分:5)

由于该主题仍然很活跃,很难找到明确的答案,除了@Max的答案之外,让我添加一些示例:

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

让我们使用array*ngFor显示为3格。

app.component.html

*ngFor 没有trackBy 的示例:

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

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

→3格将被刷新。 尝试一下,打开控制台进行验证。

*ngFor 带有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

在使用 Subscription (通常类似于我用array模式化的内容)时,它特别有用。因此,它看起来像:

   array = [];
   subscription: Subscription;

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

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

摘自文档:

  

为避免此昂贵的操作,您可以自定义默认值   跟踪算法。通过向NgForOf提供trackBy选项。   trackBy采用的函数具有两个参数:index和item。如果   给出trackBy,角度轨迹会根据   功能。

在此处了解更多信息:https://angular.io/api/common/NgForOf

在这里找到我的原始答案:https://stackoverflow.com/a/57890227/9753985

答案 2 :(得分:0)

否则您可以使用

*ngFor="a of array; index as i;"

[attr.data-target]="'#test' + i"

name="test{{i}}

答案 3 :(得分:0)

这是我在我的项目中使用的允许通过迭代模型的属性进行跟踪而无需在组件的类中编写函数的麻烦:

import { Host, Directive, Input } from "@angular/core";
import { NgForOf } from "@angular/common";

@Directive({
    selector: "[ngForTrackByProperty]"
})
export class TrackByPropertyDirective {

    private _propertyName: string = "";

    public constructor(@Host() private readonly _ngFor: NgForOf<any>) {
        this._ngFor.ngForTrackBy = (_: number, item: any) => this._propertyName ? item[this._propertyName] : item;
    }

    @Input("ngForTrackByProperty")
    public set propertyName(value: string | null) {
        // We must accept null in case the user code omitted the ": 'somePropName'" part.
        this._propertyName = value ?? "";
    }

}

用法:

<some-tag *ngFor="let item of models; trackByProperty: 'yourDiscriminantProp'">

答案 4 :(得分:-1)

这个有角度的NgFor文档会对您有所帮助。 https://angular.io/docs/ts/latest/api/common/index/NgFor-directive.html

以下代码示例

<div *ngFor="let user of users; trackBy:user?.name">
 {{user.name}} -> {{user.score}}
</div>