Inline-editable team roster
Angular · Angular · intermediate · greenfield
Adds the inline-editable roster. Rows are rendered with @for and tracked by identity so Angular reuses each row's DOM across updates instead of tearing the list down and rebuilding it while the user types.
The roster can hold a few hundred members; each row renders an avatar image and the whole list re-evaluates on every keystroke, because renaming inline updates the roster signal on each input event.
Requirements
- Render the roster as a list of rows; each row shows the member's avatar and a text input to rename the member inline.
- Typing in a row's input updates that member's name in the roster signal.
- The @for must reuse each row's DOM across updates, keyed on a stable identity, so the input the user is typing in keeps focus and the avatars don't reload on every keystroke.
Files touched
- src/app/roster/roster-editor.ts
--- src/app/roster/roster-editor.ts
+import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
+
+interface Member {
+ id: string;
+ name: string;
+ avatarUrl: string;
+}
+
+@Component({
+ selector: 'app-roster-editor',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ template: `
+ <ul class="roster">
+ @for (member of members(); track trackMember(member)) {
+ <li class="roster__row">
+ <img class="roster__avatar" [src]="member.avatarUrl" alt="" />
+ <input
+ class="roster__name"
+ [value]="member.name"
+ (input)="rename(member.id, $event)"
+ />
+ </li>
+ }
+ </ul>
+ `,
+})
+export class RosterEditor {
+ members = signal<Member[]>([
+ { id: 'm1', name: 'Ada Lovelace', avatarUrl: '/avatars/m1.png' },