bhylu.com

Advertisements

Angular Lists and Tracking

Use @for with a stable identity expression when rendering records. Tracking by a persistent ID keeps a record connected to its DOM view when order changes.

How it works

The desk can reverse its tool list without replacing the records. Each input belongs to a tool ID, not to whichever tool happens to occupy the first array position. Tracking by index can be suitable for genuinely static lists, but it is a poor default when users edit or reorder records. Older NgFor trackBy callbacks express a related maintenance concern; new @for blocks use a track expression directly.

Run the code

Use a separate empty project folder with a compatible Node version: ^22.22.3, ^24.15.0 or a supported 26+ runtime. The review uses Node 24.22.1, Angular 22.2.4, CLI/build 22.2.5 and TypeScript 6.0.3. Node 22.15.1 is too old for Angular 22. Save every download at its displayed path; browsers may keep only the basename, so recreate the src folder. Run npm install, npm run build and npm start, then open the loopback address printed by the server. Run npm test for the supplied component tests. These are local teaching projects, not Bhylu publishing files.

The small loopback server serves the compiled app and fixed, read-only tool API fixtures. It is not a database, authentication or lending service. Angular examples do not run in Bhylu’s current HTML or TypeScript playground. No Apache, MySQL or PHP configuration change is needed.

Example

src/app.ts

import { Component } from '@angular/core';
import { signal } from '@angular/core';

@Component({
  selector: 'bh-lists',
  standalone: true,
  imports: [],
  template: `<button type="button" (click)="reverse()">Reverse tools</button>
    <ul>
      @for (tool of tools(); track tool.id) {
        <li [attr.data-id]="tool.id">
          <label>{{ tool.name }}<input [value]="tool.name" /></label>
        </li>
      } @empty {
        <li>No tools</li>
      }
    </ul>
    <p data-result>{{ tools().length }} tools listed</p>`,
})
export class App {
  tools = signal([
    { id: 'DR-4', name: 'Drill' },
    { id: 'CL-2', name: 'Clamp' },
  ]);
  reverse(): void {
    this.tools.update((items) => [...items].reverse());
  }
}

Expected result

2 tools listed

The review edits the drill input, reverses the list and confirms that the same drill input node and its edited value survive. This tests identity rather than only counting rows. An ID must be unique in the rendered collection; duplicate IDs can undermine the tracking contract.

Advertisements