Angular.js:TrackBy 不会阻止完整的 *ngFor 生成的列表刷新

Angular.js: TrackBy doesn't prevent full *ngFor-generated list refresh

我有代码,可以在 ul 列表中打印一个简单句子的单词,这样您就可以删除任何项目,然后再将其插入:

  arr = ['hi', 'i', 'am', 'an' ,'item', 'too'].map(function (word) {
    return {value: word}
  });
  trackByFn (item : any) {
    return item.value;
  }
  
  removed   : any = null;
  removed_i : any = null;
  removeItem (i : number) {
    this.removed_i = i;
    this.removed = this.arr.splice(i, 1);
  }
  insert () {
    this.arr.splice(this.removed_i, 0, this.removed);
  }
<ul>
    <li *ngFor="let item of arr; let i = index; trackBy: trackByFn">
      {{item.value}}
      <button *ngIf="!removed" (click)="removeItem(i)">Remove</button>
    </li>
</ul>
<button *ngIf="removed" (click)="insert()">Insert</button>

它导致了 2 个问题:

  1. 我尝试使用 trackBy 功能。我的 trackByFn returns item.value 作为 "id" 但仍然当在调用 insert(...) 后重新呈现列表时,总是有几个 li-s 更新(通过浏览器控制台检查)!
  2. 而且插入的item不包含{{item.value}},是插进去的!

拜托,有人能解释一下这是怎么回事吗?

更新

我更改了 trackBy-function 并添加了一个带有 full_refresh() 反应的按钮。

<button (click)="full_refresh()">Random refresh</button>

full_refresh () {
    var prevSource = this.source, prevLength = prevSource.length;
    var source = this.source = new Array(prevLength);
    for (let i = 0; i < prevLength; i++)
      source[i] = {value: prevSource[i].value + prevSource[i].value};
  }

  trackBy (i : number, word : any) {
    return i; // UPDATE 3: corrected according to Siddhant's note
  }

所以,现在我已经更改了 trackBy-function(现在它 returns index 有目的)并添加了一个完整的刷新按钮,每个按钮都将 旧数组 数组对象 替换为 全新的 。所以,新的数组对象旧的无关,所以==应该returnfalse。那么 Angular 应该检查 trackBy-function,对吧?而trackBy-functionreturns数组index,所以每个new object都应该被识别与数组中相应的 old object “相同”,对吧?但我仍然完全刷新了元素!而且,控制台中会弹出一个错误:

无法绑定到 'ngForNgTrackBy',因为它不是 'li' 的已知 属性。

更新 2:

刚刚发现 trackBy 函数根本没有被调用。当我将 console.log(...) 添加到其中时,当我提交 full_refresh 时,不会记录任何内容,这会更改 source ,然后通过 ngFor.

打印为列表

此外,当我像这样更改函数名称时(请参阅 ngTrackBy: trackByByeByeByengFor 语句的末尾),应用程序已编译,但在控制台中出现错误:无法绑定到 'ngForNgTrackBy' 因为它不是 'li' 的已知 属性。

<ul>
  <li *ngFor="let word of source; let i = index; trackBy: trackByByeByeBye">
  <!--there should be trackBy <<actual property>> : trackBy <<corresponding method>>-->
    {{word.value}}
    <button (click)="remove(i)">Remove</button>
  </li>
</ul>

回答

请看Saddhant回复的最后一条评论!它在那里! :) 您在上面看到了代码,它已经被更正了!祝您愉快! :)

{{item.value}} 插入后不打印值的原因是因为插入的值是一个数组而不是包含 value 属性.

的对象
// splice returns an array of deleted elements
this.removed = this.arr.splice(i, 1);
// this.removed would be [{value: 'deletedValue'}]

// You can use this.removed[0] when inserting
this.arr1.splice(this.removed_i, 0, this.removed[0]);
// In case if this.removed contains multiple elements, you can use spread operator as ...this.removed

在您的示例中,使用 trackBy 并没有任何实际帮助,也不是必需的,因为您正在改变相同的数组并使用相同的对象引用。由于数组中的对象引用保持不变,Angular 自动将能够处理不必要的 re-creation 个 DOM 个节点。

此外,在您的案例中编写的 trackByFn 将无法按预期工作,因为传递给 trackByFn 的第一个参数是索引。

编写函数的正确方法是:

trackByFn (index: number, item : any) {
  return item.value;
}

编辑:为新问题添加到post

由于您现在正在创建一个带有新引用的新数组,因此在这种情况下使用 trackBy 是有意义的。

关于元素刷新的原因? 您在 trackByFn 中使用 word.value,并且由于您在新对象中更改了 value,Angular 会将其视为新记录,re-create 元素。

// The 'value' which is being used in trackByFn is now changed
{value: prevSource[i].value + prevSource[i].value}