Skip to content

Commit 6701817

Browse files
committed
docs(material): add autocomplete loading example
Document asynchronous autocomplete loading and empty states using signals and non-interactive status text.
1 parent bcb3e79 commit 6701817

5 files changed

Lines changed: 91 additions & 0 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.example-form {
2+
min-width: 150px;
3+
max-width: 500px;
4+
width: 100%;
5+
}
6+
7+
.example-full-width {
8+
width: 100%;
9+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<form class="example-form">
2+
@let options = filteredOptions | async;
3+
<mat-form-field class="example-full-width">
4+
<mat-label>State</mat-label>
5+
<input type="text"
6+
placeholder="Search states"
7+
aria-label="State"
8+
matInput
9+
[formControl]="myControl"
10+
[matAutocomplete]="auto">
11+
<mat-autocomplete #auto="matAutocomplete">
12+
@if (!isLoading()) {
13+
@for (option of options ?? []; track option) {
14+
<mat-option [value]="option">{{option}}</mat-option>
15+
}
16+
}
17+
</mat-autocomplete>
18+
<mat-hint aria-live="polite">
19+
@if (isLoading()) {
20+
Loading...
21+
} @else if (options?.length === 0) {
22+
No results found
23+
}
24+
</mat-hint>
25+
</mat-form-field>
26+
</form>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import {AsyncPipe} from '@angular/common';
2+
import {Component, signal} from '@angular/core';
3+
import {FormControl, ReactiveFormsModule} from '@angular/forms';
4+
import {MatAutocompleteModule} from '@angular/material/autocomplete';
5+
import {MatFormFieldModule} from '@angular/material/form-field';
6+
import {MatInputModule} from '@angular/material/input';
7+
import {Observable, of} from 'rxjs';
8+
import {delay, finalize, startWith, switchMap} from 'rxjs/operators';
9+
10+
/** @title Autocomplete with asynchronous loading */
11+
@Component({
12+
selector: 'autocomplete-loading-example',
13+
templateUrl: 'autocomplete-loading-example.html',
14+
styleUrl: 'autocomplete-loading-example.css',
15+
imports: [
16+
MatFormFieldModule,
17+
MatInputModule,
18+
MatAutocompleteModule,
19+
ReactiveFormsModule,
20+
AsyncPipe,
21+
],
22+
})
23+
export class AutocompleteLoadingExample {
24+
myControl = new FormControl('');
25+
options = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California'];
26+
filteredOptions: Observable<string[]>;
27+
isLoading = signal(true);
28+
29+
constructor() {
30+
this.filteredOptions = this.myControl.valueChanges.pipe(
31+
startWith(''),
32+
switchMap(value => {
33+
this.isLoading.set(true);
34+
return this._filter(value || '').pipe(finalize(() => this.isLoading.set(false)));
35+
}),
36+
);
37+
}
38+
39+
private _filter(value: string): Observable<string[]> {
40+
const filterValue = value.toLowerCase();
41+
const results = this.options.filter(option => option.toLowerCase().includes(filterValue));
42+
43+
// Simulate an asynchronous request.
44+
return of(results).pipe(delay(500));
45+
}
46+
}

src/components-examples/material/autocomplete/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export {AutocompleteAutoActiveFirstOptionExample} from './autocomplete-auto-active-first-option/autocomplete-auto-active-first-option-example';
22
export {AutocompleteDisplayExample} from './autocomplete-display/autocomplete-display-example';
33
export {AutocompleteFilterExample} from './autocomplete-filter/autocomplete-filter-example';
4+
export {AutocompleteLoadingExample} from './autocomplete-loading/autocomplete-loading-example';
45
export {AutocompleteOptgroupExample} from './autocomplete-optgroup/autocomplete-optgroup-example';
56
export {AutocompleteOverviewExample} from './autocomplete-overview/autocomplete-overview-example';
67
export {AutocompletePlainInputExample} from './autocomplete-plain-input/autocomplete-plain-input-example';

src/material/autocomplete/autocomplete.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,15 @@ filter that doesn't limit matches to the beginning of the string.
4949

5050
<!-- example(autocomplete-filter) -->
5151

52+
### Showing loading and empty states
53+
54+
When autocomplete options come from an asynchronous source, keep loading and empty-state messages
55+
outside of `mat-option` so that options only represent values that users can select. The example
56+
below uses `switchMap` so stale requests are cancelled when the input changes, `finalize` to clear
57+
the loading state when each request finishes, and an `aria-live` hint to announce status changes.
58+
59+
<!-- example(autocomplete-loading) -->
60+
5261
### Setting separate control and display values
5362

5463
If you want the option's control value (what is saved in the form) to be different than the option's

0 commit comments

Comments
 (0)