Prepend a row and both columns insert one node.

Only one of them keeps your half-typed input.

track $index
the view belongs to the position
Newadawrong row
Adagracewrong row
Graceedsgerwrong row
Edsgerbarbarawrong row
Barbaraalanwrong row
Alandonaldwrong row
Donald(empty)wrong row
track row.id
the view belongs to the row
New(empty)kept
Adaadakept
Gracegracekept
Edsgeredsgerkept
Barbarabarbarakept
Alanalankept
Donalddonaldkept
shuffle, 6 rows (one run)
$indexrow.id DOM ops (added/removed)04+4 rows showing another row's state5 of 60 of 6
prepend one row
$indexrow.id DOM nodes added11 existing rows rebuilt00
3 refetches — identical data, new objects
track rowrow.id rows rebuilt90 typed textlostkept
why the wrong one is so common
NG5002: @for loop must have a "track" expression
track is mandatory, so every migrated list had to pick something. $index is the shortest thing that makes the build pass, and on a list of read-only text it does no harm at all — which is exactly how it reaches the lists where it does.
measured: Angular 21.2 LTS · DOM counts from a MutationObserver on each list · the chip is what the user typed
gdhami.net