当我读取ngFor
和trackBy
的文档(https://angular.io/api/common/NgForOf)时,我以为我知道Angular仅在trackBy函数返回的值发生更改时才会重做DOM,但是当我在这里(https://stackblitz.com/edit/angular-playground-bveczb)玩游戏时,我发现我根本不了解它。这是我的代码的基本部分:
export class AppComponent {
data = [
{ id: 1, text: 'one' },
{ id: 2, text: 'two' },
{ id: 3, text: 'three' },
];
toUpper() {
this.data.map(d => d.text = d.text.toUpperCase());
}
trackByIds (index: number, item: any) {
return item.id;
};
}
并且:
<div *ngFor="let d of data; trackBy: trackByIds">
{{ d.text }}
</div>
<button (click)=toUpper()>To Upper Case</button>
我期望单击按钮应该不将列表从小写更改为大写,但是确实如此。我以为我在trackByIds
中的trackBy
中使用了*ngFor
函数,并且由于trackByIds
仅检查项目的id
属性,因此更改id以外的任何内容都不应导致DOM重做。我想我的理解是错误的。
答案 0 :(得分:2)
如果for
似乎不起作用:
https://angular.io/api/core/TrackByFunction
trackBy
即使您仅使用对象派生“ tracked by”表达式,您的函数也必须将索引作为第一个参数。
interface TrackByFunction<T> {
(index: number, item: T): any
}
trackByProductSKU(_index: number, product: { sku: string })
{
return product.sku;
}
循环上方的控件中添加<input/>
-(是的-只是一个空文本框)*ngFor
与您的基本问题无关)。trackBy
函数为每一行返回一个唯一值:trackBy
像这样按循环显示值。这将消除任何愚蠢的错误-例如通过属性错误获取轨道的名称或大小写。空的<li *ngFor="let item of lineItems; trackBy: trackByProductSKU">
<input />
Tracking value: [{{ trackByProductSKU(-1, item) }}]
</li>
元素是有意的
如果一切正常,您应该能够在每个输入框中键入内容,触发列表中的更改,并且它不会丢失您键入的值。
答案 1 :(得分:1)
trackBy
函数确定何时应重新呈现由ngFor
循环创建的div元素(由DOM中的新元素替换)。请注意,Angular始终可以通过修改元素的属性或属性来更新元素。更新元素并不意味着用新元素替换它。这就是为什么即使不重新渲染div元素也将文本设置为大写的原因。
默认情况下,如果不指定trackBy
函数,则当相应项的值更改时,div元素将重新呈现。在当前情况下,就是将data
数组项目替换为另一个对象(项目“值”是对象引用)时;例如,执行以下方法后:
recreateDataArray() {
this.data = this.data.map(x => Object.assign({}, x));
}
现在,使用trackBy
函数返回数据项id
,您可以告诉ngFor
循环在以下情况下重新呈现div元素:相应的项目更改。因此,在执行上述id
方法后,现有的div元素将保留在DOM中,但是在运行以下方法后,它们将被新的div元素替换:
recreateDataArray
您可以尝试使用this stackblitz。复选框允许打开/关闭incrementIds() {
this.data.forEach(x => { x.id += 10; });
}
逻辑,并且控制台消息指示何时重新渲染div元素。 “设置红色文本”按钮可以直接更改DOM元素的样式。您知道红色div元素的内容变成黑色时已重新渲染。
答案 2 :(得分:0)
trackBy实际上用于防止一次又一次地重新呈现DOM中的相同元素。不能像您正在使用的那样使用它。让我详细说明。如果你有这样的数组:
data = [
{ id: 1, text: 'one' },
{ id: 2, text: 'two' },
{ id: 3, text: 'three' },
];
然后单击按钮,您将更改数组,如下所示:
changeArray() {
this.data = [
{ id: 1, text: 'one' },
{ id: 2, text: 'two' },
{ id: 3, text: 'three' },
{ id: 4, text: 'four' },
{ id: 5, text: 'five' },
];
}
trackByIds (index: number, item: any) {
return item.id;
};
}
并且:
<div *ngFor="let d of data; trackBy: trackByIds">
{{ d.text }}
</div>
<button (click)="changeArray()>Change Array</button>
在单击按钮时调用changeArray()时。然后,trackBy保证仅将具有新ID的项目添加到DOM中,而不会重新呈现先前的项目,即,具有ID 1-3的项目不会在DOM中再次呈现。如果您认为可以使用它来防止操纵,那您就错了。希望你能得到我! 证明: 单击按钮之前 单击按钮后,只有突出显示的内容会更改