[英]How to use `trackBy` with `ngFor`
I can't really understand what I should return from trackBy
.我真的不明白我应该从trackBy
返回什么。 Based on some examples I've seen on the web, I should return the value of some property on the object.根据我在网上看到的一些示例,我应该返回对象上某些属性的值。 Is it right?这样对吗? Why should I get index
as a parameter?为什么要获取index
作为参数?
For example, in the following case:例如,在以下情况下:
Component.component.ts组件.component.ts
constructor() {
window.setInterval(() => this.users = [
{ name: 'user1', score: Math.random() },
{ name: 'user2', score: Math.random() }
],
1000);
}
userByName(index, user) {
return user.name;
}
Component.template.html组件.template.html
<div *ngFor="let user of users; trackBy:userByName">
{{user.name}} -> {{user.score}}
</div>
The objects shown in this template are still updated despite the name being unchanged.尽管名称未更改,但此模板中显示的对象仍会更新。 Why?为什么?
On each ngDoCheck
triggered for the ngForOf
directive, Angular checks what objects have changed.在为ngForOf
指令触发的每个ngDoCheck
上,Angular 都会检查哪些对象发生了变化。 It uses differs for this process and each differ uses the trackBy
function to compare the current object with the new one.它在这个过程中使用了差异,每个差异都使用trackBy
函数将当前对象与新对象进行比较。 The default trackBy
function tracks items by identity:默认trackBy
函数按身份跟踪项目:
const trackByIdentity = (index: number, item: any) => item;
It receives the current item and should return some value.它接收当前项目并且应该返回一些值。 Then the value returned by the function is compared against the value this function returned the last time.然后将函数返回的值与此函数上次返回的值进行比较。 If the value changes, the differ reports a change.如果值发生变化,diff 报告变化。 So if the default function returns object references, it will not match the current item if the object reference has changed.因此,如果默认函数返回对象引用,则如果对象引用发生更改,它将与当前项不匹配。 So you can provide your custom trackBy
function that will return something else.因此,您可以提供将返回其他内容的自定义trackBy
函数。 For example, some key value of the object.比如对象的一些键值。 If this key value matches the previous one, then Angular will not detect the change.如果此键值与前一个键值匹配,则 Angular 将不会检测到更改。
The syntax ...trackBy:userByName
is no longer supported.语法...trackBy:userByName
不再受支持。 You must now provide a function reference.您现在必须提供函数引用。 Here is the basic example:这是基本示例:
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;
}
Although the object reference changes, the DOM is not updated.尽管对象引用发生了变化,但 DOM 并未更新。 Here is the plunker.这是笨蛋。 If you're curious how ngFor
works under the hood, read this answer .如果您好奇ngFor
如何在后台工作, 请阅读此答案。
As this topic is still active & finding a clear answer is difficult let me add few examples in addition to @Max 's answer:由于这个话题仍然很活跃,而且很难找到一个明确的答案,所以除了@Max的答案之外,我还要添加几个例子:
app.component.ts 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 divs using *ngFor
.让我们使用*ngFor
将array
显示为 3 个 div。
app.component.html app.component.html
Example of *ngFor
without trackBy :没有 trackBy的*ngFor
示例:
<div *ngFor="let e of array;">
{{e.id}} - {{e.name}}
</div>
<button (click)="foo()">foo</button>
What happens if we click the foo
button ?如果我们点击foo
按钮会发生什么?
→ The 3 divs will be refreshed. → 3 个 div 将被刷新。 Try it yourself, open your console to verify.自己尝试一下,打开你的控制台进行验证。
Example of *ngFor
with trackBy :带有 trackBy的*ngFor
示例:
<div *ngFor="let e of array; trackBy: identify">
{{e.id}} - {{e.name}}
</div>
<button (click)="foo()">foo</button>
What happens if we click the foo
button ?如果我们点击foo
按钮会发生什么?
→ Only the first div will be refreshed. → 只有第一个 div 会被刷新。 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. → 这里不需要使用trackBy
。
It's especially useful when using a Subscription which often looks like what I schematized with array
.它在使用通常看起来像我用array
模式化的Subscription时特别有用。 So it would look like:所以它看起来像:
array = [];
subscription: Subscription;
ngOnInit(): void {
this.subscription = this.fooService.getArray().subscribe(data => {
this.array = data;
});
}
identify(index, item) {
return item.id;
}
From the documentation:从文档中:
To avoid this expensive operation, you can customize the default tracking algorithm.为避免这种昂贵的操作,您可以自定义默认跟踪算法。 by supplying the trackBy option to NgForOf.通过向 NgForOf 提供 trackBy 选项。 trackBy takes a function that has two arguments: index and item. trackBy 接受一个有两个参数的函数:index 和 item。 If trackBy is given, Angular tracks changes by the return value of the function.如果给出了 trackBy,Angular 会根据函数的返回值跟踪变化。
Read more here: https://angular.io/api/common/NgForOf在此处阅读更多信息: https ://angular.io/api/common/NgForOf
Find my original answer here: https://stackoverflow.com/a/57890227/9753985在这里找到我的原始答案: https ://stackoverflow.com/a/57890227/9753985
Here's what I use in my projects to allow tracking by a property of the iterated model without the hassle of writing a function in the component's class :这是我在我的项目中使用的,以允许通过迭代模型的属性进行跟踪,而无需在组件的类中编写函数:
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 ?? "";
}
}
Usage :用法 :
<some-tag *ngFor="let item of models; trackByProperty: 'yourDiscriminantProp'">
app.component.html app.component.html
<button class="btn btn-warning" (click)="loadCourses()">LoadCourses</button>
<ul>
<li *ngFor="let course of courses; trackBy:trackCourse">
{{course.name}}
</li>
</ul>
app.component.ts app.component.ts
loadCourses() {
this.courses = [
{id:1, name:'cour1'},
{id:2, name:'cour2'},
{id:3, name:'cour3'}
]
};
trackCourse(index : number, course: any) {
return course ? course.id : undefined;
};
Reference Code With Mosh You can find in Directives Section Mosh的参考代码您可以在指令部分找到
The purpose of using the trackBy is to set the identity of elements in an iterable.使用 trackBy 的目的是在可迭代对象中设置元素的标识。 If Angular sees two elements with the same identity, it will proceed to check the content of the elements and will only repaint if the content is changed.如果 Angular 看到两个具有相同标识的元素,它将继续检查元素的内容,并且只有在内容发生更改时才会重新绘制。 Without the identity, Angular will rely on the object reference of the elements that usually change even when the content are the same, and thus Angular will repaint the elements because of different references.如果没有标识,Angular 将依赖元素的对象引用,即使内容相同,通常也会发生变化,因此 Angular 会因为引用不同而重新绘制元素。
This angular NgFor
document will help you.这个有角度NgFor
文档将为您提供帮助。 https://angular.io/docs/ts/latest/api/common/index/NgFor-directive.html 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>
else you can use否则你可以使用
*ngFor="a of array; index as i;"
and和
[attr.data-target]="'#test' + i"
and和
name="test{{i}}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.