当我阅读and的文档(https://angular.io/api/common/NgForOf)时,我想我明白如果 trackBy 函数返回的值发生变化,Angular 只会重做 DOM,但是当我玩它在这里(https://stackblitz.com/edit/angular-playground-bveczb),我发现我实际上根本不理解它。这是我的代码的重要部分:ngFortrackBy
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;
};
}
Run Code Online (Sandbox Code Playgroud)
和:
<div *ngFor="let d of data; trackBy: trackByIds">
{{ d.text }}
</div>
<button (click)=toUpper()>To Upper Case</button>
Run Code Online (Sandbox Code Playgroud)
我预料到的点击按钮应该不是单从小写改为上,但它确实。我以为我trackByIds在trackByin 中使用了该函数*ngFor,并且由于trackByIds它只检查id项目的属性,因此更改 id 以外的任何内容都不应导致 DOM 重做。我想我的理解是错误的。
Sim*_*ver 17
如果trackBy似乎不起作用:
https://angular.io/api/core/TrackByFunction
interface TrackByFunction<T> {
(index: number, item: T): any
}
Run Code Online (Sandbox Code Playgroud)
即使您仅使用对象来派生“跟踪依据”表达式,您的函数也必须将索引作为第一个参数。
trackByProductSKU(_index: number, product: { sku: string })
{
// add a breakpoint or debugger statement to be 100% sure your
// function is actually being called (!)
debugger;
return product.sku;
}
Run Code Online (Sandbox Code Playgroud)
<input/>控件*ngFor- (是 - 只是一个空文本框)trackBy与根本问题无关)。<input/>如果有多个嵌套循环,则可以放在每个“级别”。只需在每个框中键入一个值,然后查看在执行导致问题的任何操作时保留哪些值。trackBy函数为每一行返回唯一的值:<li *ngFor="let item of lineItems; trackBy: trackByProductSKU">
<input />
Tracking value: [{{ trackByProductSKU(-1, item) }}]
</li>
Run Code Online (Sandbox Code Playgroud)
像这样在循环内按值显示曲目。这将消除任何愚蠢的错误 - 例如按属性获取不正确的轨道名称或大小写。空input元素是故意的
如果一切正常,您应该能够在每个输入框中键入内容,触发列表中的更改,并且它不应该丢失您键入的值。
trackByIdentity如果您未指定 trackBy 函数,这是默认行为(来自)。// angular/core/src/change_detection/differs/default_iterable_differ.ts
const trackByIdentity = (index: number, item: any) => item;
export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChanges<V> {
constructor(trackByFn?: TrackByFunction<V>) {
this._trackByFn = trackByFn || trackByIdentity;
}
Run Code Online (Sandbox Code Playgroud)
假设您正在使用product: { sku: string }trackBy 函数,并且出于某种原因,产品不再具有该属性集。(也许它变成了SKU或者有一个额外的级别。)
如果您product.sku从函数返回并且它为空,那么您将得到一些意外的行为。
该trackBy函数确定何时ngFor应该重新渲染循环创建的 div 元素(由 DOM 中的新元素替换)。请注意,Angular 始终可以通过修改元素的属性或属性来更新元素的变化检测。更新元素并不意味着用新元素替换它。这就是为什么将文本设置为大写会反映在浏览器中,即使 div 元素没有重新渲染。
默认情况下,在不指定trackBy函数的情况下,当相应的 item 值发生变化时,将重新渲染 div 元素。在目前的情况下,这将是当data数组项被不同的对象替换时(项“值”是对象引用);例如在执行以下方法后:
recreateDataArray() {
this.data = this.data.map(x => Object.assign({}, x));
}
Run Code Online (Sandbox Code Playgroud)
现在,使用trackBy返回数据 item的函数id,您告诉ngFor循环id在相应项目的属性更改时重新呈现 div 元素。因此,执行上述recreateDataArray方法后,现有的 div 元素将保留在 DOM 中,但在运行以下方法后,它们将被新元素替换:
incrementIds() {
this.data.forEach(x => { x.id += 10; });
}
Run Code Online (Sandbox Code Playgroud)
你可以试验这个 stackblitz。复选框允许打开/关闭trackByIds逻辑,控制台消息指示何时重新渲染 div 元素。“设置红色文本”按钮直接改变DOM元素的样式;您知道红色 div 元素在其内容变为黑色时已重新呈现。
trackBy 实际上是用来防止 DOM 中相同元素一次又一次地重新渲染。它不能像你正在使用的那样使用。让我详细说明一下。如果你有这样的数组:
data = [
{ id: 1, text: 'one' },
{ id: 2, text: 'two' },
{ id: 3, text: 'three' },
];
Run Code Online (Sandbox Code Playgroud)
单击按钮后,您可以更改数组,如下所示:
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;
};
}
Run Code Online (Sandbox Code Playgroud)
和:
<div *ngFor="let d of data; trackBy: trackByIds">
{{ d.text }}
</div>
<button (click)="changeArray()>Change Array</button>
Run Code Online (Sandbox Code Playgroud)
当您点击按钮调用changeArray()时。然后,trackBy 保证只有具有新 id 的项目才会被添加到 DOM,并且之前的项目不会被重新渲染,即具有 id 1-3 的项目不会在 DOM 中再次渲染。如果您认为可以使用它来防止操纵,那么您就错了。希望你能得到我!证明:点击按钮之前
单击按钮后,仅突出显示的内容发生变化
| 归档时间: |
|
| 查看次数: |
4435 次 |
| 最近记录: |