当元素从有界数组拼接时,ngFor 的行为与 trackBy index 和 trackBy Id 不同

Behaviour of ngFor differs with trackBy index and trackBy Id when element is spliced from bounded array

我有一小部分用户。现在,我为每个数组元素添加了一个独立的组件(用户选择),它没有来自父组件的数据绑定并显示自己的数据。

当我删除任何索引处的用户并使用 trackBy 索引时,最后一个用户选择元素将被删除,而不是删除索引处的元素。

但是,当我使用 trackBy id(唯一标识符)时,它会按预期工作。

我无法理解 trackBy 在这两种情况下有何不同。

这是相同的演示 - https://stackblitz.com/edit/angular-ivy-u4pwrl

app.component.html

<h1>By Id</h1>
<div *ngFor="let user of users1; let idx = index; trackBy: trackById">
  <app-user-choice></app-user-choice>
  <button (click)="deleteUser1(idx)">Delete</button>
</div>
<h1>By index</h1>
<div *ngFor="let user of users2; let idx = index; trackBy: trackByIndex">
  <app-user-choice></app-user-choice>
  <button (click)="deleteUser2(idx)">Delete</button>
</div>

app.component.ts

export class AppComponent {
  name = 'Angular ' + VERSION.major;
  users1 = [
    {
      name: 'a',
      id: 1
    },
    {
      name: 'b',
      id: 2
    },
    {
      name: 'c',
      id: 3
    },
    {
      name: 'd',
      id: 4
    },
    {
      name: 'e',
      id: 5
    }
  ];
  users2 = [
    {
      name: 'a',
      id: 1
    },
    {
      name: 'b',
      id: 2
    },
    {
      name: 'c',
      id: 3
    },
    {
      name: 'd',
      id: 4
    },
    {
      name: 'e',
      id: 5
    }
  ];
  num = 0;
  deleteUser1(idx) {
    this.users1.splice(idx, 1);
  }
  deleteUser2(idx) {
    this.users2.splice(idx, 1);
  }
  trackById(index, item) {
    return item.id;
  }
  trackByIndex(index) {
    return index;
  }
}

用户-choice.component.html

<p>
  user-info works! {{index}}
</p>

用户-choice.component.ts

export class UserChoiceComponent implements OnInit {
  index = Math.random() * 10;
  constructor() {}

  ngOnInit() {}
}

trackBy 应该防止 Angular 在预期只有一个更改时跟踪可迭代对象中的所有更改。

主要问题是,您的 (click) 指令在 track by 发挥作用之前被触发。您单击删除按钮,项目将被删除,然后 trackBy 检查更改。

使用trackById()-方法,您始终return 已删除对象的实际id。例如,您单击具有 id 3 和 trackById returns 3 的对象。因此 Angular 可以检测到已更改的对象并将其从 UI 中正确删除.

trackByIndex() returns Angular 自己的循环索引。现在想象你有一个对象列表,其中 ids 从 0 到 4。只有在开始时 Angular 的索引和对象的 id 匹配。现在你删除了一个对象。比方说id3的对象,那么对象之间就没有了id3,但是Angular仍然监视着index3的对象。而且,对象索引 0 到 2 的 ID 为 0 到 2,但索引 3 的对象的 ID 为 4,因为索引总是根据条目的顺序进行调整。

Angular 不知道哪个对象实际发生了变化,因为它监视了错误的项目。因此使用默认行为并从列表中删除最后一个条目。这就是 byIndex 方法在这里根本不起作用的原因。


一个例子

开始

name:       a
Angular id: 0
object id:  0

name:       b
Angular id: 1
object id:  1

name:       c
Angular id: 2
object id:  2

name:       d
Angular id: 3
object id:  3

name:       e
Angular id: 4
object id:  4

现在您在 ID 为 3 的对象上单击 delete

当你这样做的时候。该项目首先从列表中删除,然后 trackBy 才开始工作。但是 id 3 不再有对象。

name:       a
Angular id: 0
object id:  0

name:       b
Angular id: 1
object id:  1

name:       c
Angular id: 2
object id:  2

name:       e
Angular id: 3
object id:  4

你看。主要问题是,使用 Angular 的静态有序索引在这里不起作用,因为对象的索引永远不会匹配。您必须使用对象自己的 ID。