角度ngFor trackBy无法正常工作

时间:2019-06-19 23:26:49

标签: angular

当我读取ngFortrackBy的文档(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重做。我想我的理解是错误的。

3 个答案:

答案 0 :(得分:2)

如果for似乎不起作用:

1)确保为trackBy函数使用正确的签名

https://angular.io/api/core/TrackByFunction

trackBy

即使您仅使用对象派生“ tracked by”表达式,您的函数也必须将索引作为第一个参数。

interface TrackByFunction<T> {
  (index: number, item: T): any
}

2)确保未重绘整个控件(包含* ngFor),这可能是由于其他原因引起的。

  • trackByProductSKU(_index: number, product: { sku: string }) { return product.sku; } 循环上方的控件中添加<input/>-(是的-只是一个空文本框)
  • 加载页面并在文本框中输入内容
  • 添加/删除列表中的项目-或执行任何触发更改的操作
  • 如果文本框的内容消失了,则意味着您正在重新绘制整个容器控件(换句话说,您的*ngFor与您的基本问题无关)。

3)确保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中再次呈现。如果您认为可以使用它来防止操纵,那您就错了。希望你能得到我! 证明: 单击按钮之前 Before changeArray clicked 单击按钮后,只有突出显示的内容会更改

After button click only four and five shown highlighted as they're only change

如果通过单击按钮将一个转换为一个 Proof if one is converted to ONE