Make user search live as you type

Angular · Angular · intermediate · modification

Turns the manual search into a live typeahead. Instead of a button, the component now reacts to the reactive-form valueChanges, debounces the input, and maps each query to the /api/users request, rendering the matches. Subscription is torn down with takeUntilDestroyed so nothing leaks. Tested locally by typing several queries and confirming the list updates on every change.

Used in the admin panel header. Production users often search from slow mobile networks, so requests for different queries are routinely in flight at the same time.

Requirements

Files touched

--- src/app/components/user-search.ts
 import { HttpClient } from '@angular/common/http';
 import { FormControl, ReactiveFormsModule } from '@angular/forms';
+import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
+import { of } from 'rxjs';
+import { catchError, debounceTime, mergeMap } from 'rxjs/operators';
 
 interface User {
   template: `
     <input [formControl]="query" placeholder="Search users" />
-    <button (click)="search()">Search</button>
     <ul>
       @for (u of results(); track u.id) {
   results = signal<User[]>([]);
 
-  search(): void {
-    this.http
-      .get<User[]>(`/api/users?q=${this.query.value}`)
+  constructor() {
+    this.query.valueChanges
+      .pipe(
+        debounceTime(200),
+        mergeMap((q) => this.http.get<User[]>('/api/users', { params: { q } }).pipe(catchError(() => of<User[]>([])))),
+        takeUntilDestroyed(),
+      )
       .subscribe((users) => this.results.set(users));
   }

Review this PR

Angular practice