Enciclopédia de Erros

Expressão de tracking causou recriação da estrutura DOM.

A expressão de tracking de identidade especificada no loop @for causou recriação do DOM correspondente a todos os itens. Esta é uma operação muito custosa que comumente ocorre ao trabalhar com estruturas de dados imutáveis. Por exemplo:

@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 }));  }}

No exemplo fornecido, a lista inteira com todas as views (nós DOM, diretivas Angular, components, queries, etc.) são recriadas (!) após alternar o status "done" dos itens. Aqui, uma atualização de binding relativamente barata para a propriedade done seria suficiente.

Além de ter uma alta penalidade de performance, recriar a árvore DOM resulta em perda de estado nos elementos DOM (ex.: foco, seleção de texto, sites carregados em um iframe, etc.).

Corrigindo o erro

Altere a expressão de tracking de forma que ela identifique uniquamente um item em uma coleção, independentemente de sua identidade de objeto. No exemplo discutido, a expressão track correta usaria a propriedade única 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 }));  }}