Coding exercise: build an autocomplete dropdown in Angular.

Angular practical coding · Freshers and experienced

Model answer

Combine a form control, debounced valueChanges, a list of matches and keyboard control. Angular Material has an autocomplete, but be ready to build a simple one.

@Component({
  selector: 'app-autocomplete',
  imports: [ReactiveFormsModule],
  template: `
    <input role="combobox" [attr.aria-expanded]="matches().length > 0" [formControl]="q"
           (keydown.arrowdown)="move(1, $event)" (keydown.arrowup)="move(-1, $event)" (keydown.enter)="choose(matches()[active()])" />
    @if (matches().length) {
      <ul role="listbox">
        @for (m of matches(); track m; let i = $index) {
          <li role="option" [attr.aria-selected]="i === active()" (mousedown)="choose(m)">{{ m }}</li>
        }
      </ul>
    }`,
})
export class Autocomplete {
  options = input.required<string[]>();
  picked = output<string>();
  q = new FormControl('', { nonNullable: true });
  active = signal(0);
  private term = toSignal(this.q.valueChanges.pipe(debounceTime(200)), { initialValue: '' });
  matches = computed(() => {
    const t = this.term().toLowerCase();
    return t ? this.options().filter((o) => o.toLowerCase().includes(t)).slice(0, 6) : [];
  });
  move(d: number, e: Event) { e.preventDefault(); this.active.update((a) => Math.max(0, Math.min(a + d, this.matches().length - 1))); }
  choose(v?: string) { if (v) { this.q.setValue(v, { emitEvent: false }); this.picked.emit(v); this.active.set(0); } }
}

For remote data, replace the computed with a switchMap to the API so older requests are cancelled.

Tips

Practise it yourself in the React and Angular code playground.

Learn this topic

Angular Tutorial: Beginner to Advanced .

Sign in to add your own answer.

More questions