@for, track and @empty
Greenhouse shows a list of plants, and the list changes: plants are added, removed and watered many times a day. Angular must turn an array into elements, and then, on every change, work out which elements to keep. The @for block does both, as long as you tell it how to recognise each plant.
The rule
One block per item, tracked by something that never changes.
<ul>
@for (p of plants(); track p.id) {
<li [class.last]="$last">{{ $index + 1 }}. {{ p.name }}</li>
} @empty {
<li>No plants yet.</li>
}
</ul>p of plants()names each itemp.track p.idis required. It gives Angular a stable identity for each item.@emptyis optional and renders when the collection has no items.
Inside the block, Angular provides helpful values: $index, $first, $last, $even, $odd and $count. You can rename them if loops are nested: @for (p of plants(); track p.id; let i = $index).
Why is track required? When the array changes, Angular compares the tracked keys of the old and new items. Same key: reuse the existing element and just update its text. New key: create an element. Missing key: remove it. With a good key, watering one plant touches one <li>. With a bad key, Angular throws away and rebuilds rows, and any focus, scroll position or half-typed text inside them is lost.
What to track:
- A unique id from your data. Best choice.
$indexfor a fixed list that never reorders, like a static list of weekdays.- Never a value that changes, such as
p.lastWatered, and avoid the whole object when you replace objects with new copies.
The shortcut
Ask "What would I call this item on the phone?" Not its colour or when it was last watered, but its name tag that never changes. That is your track expression.
Before
Plants 7, 12 and 15 are shown, tracked by id.
This replaces the older *ngFor="let p of plants; trackBy: byId", where tracking was optional and often forgotten.
Drills
Answer fast — trust the rule.
Which @for is valid?
What is the best track for plants that each have a unique id?
When does the @empty block render?
How would you print "1.", "2.", "3." before each plant name?
Rows lose their typed text every time any plant is watered. What is the likely cause?
Remember
@for (item of list; track key) { } @empty { };trackis required.- Track a unique, unchanging id;
$indexonly for fixed lists. - Helpers:
$index,$first,$last,$even,$odd,$count.
