L'expression de suivi par identité spécifiée dans la boucle @for a provoqué la recréation du DOM correspondant à tous les éléments. C'est une opération très coûteuse, qui survient souvent lorsque l'on travaille avec des structures de données immuables. Par exemple :
@Component({
template: `
<button (click)="toggleAllDone()">All done!</button>
<ul>
@for (todo of todos; track todo) {
<li>{{ todo.task }}</li>
}
</ul>
`,
})
export class App {
todos = [
{id: 0, task: 'understand trackBy', done: false},
{id: 1, task: 'use proper tracking expression', done: false},
];
toggleAllDone() {
this.todos = this.todos.map((todo) => ({...todo, done: true}));
}
}
Dans l'exemple ci-dessus, toute la liste avec toutes ses vues (nœuds du DOM, directives Angular, composants, queries, etc.) est recréée (!) après le basculement du statut « done » des éléments. Ici, une mise à jour relativement peu coûteuse du binding de la propriété done suffirait.
En plus d'un coût élevé en performances, recréer l'arbre du DOM entraîne une perte d'état dans les éléments du DOM (par ex. : focus, sélection de texte, sites chargés dans une iframe, etc.).
Corriger l'erreur
Modifiez l'expression de suivi pour qu'elle identifie de manière unique un élément d'une collection, indépendamment de l'identité de son objet. Dans l'exemple présenté, l'expression de suivi correcte utiliserait la propriété unique id (item.id) :
@Component({
template: `
<button (click)="toggleAllDone()">All done!</button>
<ul>
@for (todo of todos; track todo.id) {
<li>{{ todo.task }}</li>
}
</ul>
`,
})
export class App {
todos = [
{id: 0, task: 'understand trackBy', done: false},
{id: 1, task: 'use proper tracking expression', done: false},
];
toggleAllDone() {
this.todos = this.todos.map((todo) => ({...todo, done: true}));
}
}