md-autocomplete spec card
Identity · when to use / when NOT · decision cues · behavioural contract · do/don't · anti-patterns · full API. Paste into your agent when you're implementing with this component.
Type to filter, then choose. A text field with a suggestion menu: single or multiple selection, optional free-text values, custom filtering, async loading, and virtualization for large option sets.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-autocomplete label="City" placeholder="Start typing…" style="inline-size: 280px;">
<md-select-option value="par">Paris</md-select-option>
<md-select-option value="ber">Berlin</md-select-option>
<md-select-option value="mad">Madrid</md-select-option>
<md-select-option value="rom">Rome</md-select-option>
<md-select-option value="lis">Lisbon</md-select-option>
<md-select-option value="ath">Athens</md-select-option>
</md-autocomplete>import { MdAutocomplete, MdSelectOption } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdAutocomplete label="City" placeholder="Start typing…" style={{ inlineSize: '280px' }}>
<MdSelectOption value="par">Paris</MdSelectOption>
<MdSelectOption value="ber">Berlin</MdSelectOption>
<MdSelectOption value="mad">Madrid</MdSelectOption>
<MdSelectOption value="rom">Rome</MdSelectOption>
<MdSelectOption value="lis">Lisbon</MdSelectOption>
<MdSelectOption value="ath">Athens</MdSelectOption>
</MdAutocomplete>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-autocomplete label="City" placeholder="Start typing…" style="inline-size: 280px;">
<md-select-option value="par">Paris</md-select-option>
<md-select-option value="ber">Berlin</md-select-option>
<md-select-option value="mad">Madrid</md-select-option>
<md-select-option value="rom">Rome</md-select-option>
<md-select-option value="lis">Lisbon</md-select-option>
<md-select-option value="ath">Athens</md-select-option>
</md-autocomplete><script setup>
import '@awc-ui/core/define';
</script>
<template>
<md-autocomplete label="City" placeholder="Start typing…" style="inline-size: 280px;">
<md-select-option value="par">Paris</md-select-option>
<md-select-option value="ber">Berlin</md-select-option>
<md-select-option value="mad">Madrid</md-select-option>
<md-select-option value="rom">Rome</md-select-option>
<md-select-option value="lis">Lisbon</md-select-option>
<md-select-option value="ath">Athens</md-select-option>
</md-autocomplete>
</template><script>
import '@awc-ui/core/define';
</script>
<md-autocomplete label="City" placeholder="Start typing…" style="inline-size: 280px;">
<md-select-option value="par">Paris</md-select-option>
<md-select-option value="ber">Berlin</md-select-option>
<md-select-option value="mad">Madrid</md-select-option>
<md-select-option value="rom">Rome</md-select-option>
<md-select-option value="lis">Lisbon</md-select-option>
<md-select-option value="ath">Athens</md-select-option>
</md-autocomplete>
Already installed? See the
Installation guide for one-time package setup
(core + tokens, fonts). Each tab below shows two patterns for using
md-autocomplete in your project: Option A registers every AWC UI
component at once (simplest), Option B imports only this component for
tree-shake-friendly bundles.
<!-- ─── Option A: global registration (all components) ─── -->
<script type="module">
import '@awc-ui/core/define';
</script>
<!-- ─── Option B: single import (tree-shake only md-autocomplete) ─── -->
<script type="module">
import '@awc-ui/core/components/md-autocomplete';
</script>
<md-autocomplete></md-autocomplete>// ─── Option A: typed React wrapper (registers all components) ───
// Importing from '@awc-ui/react' calls defineCustomElements() as a
// side effect, so every md-* element becomes available in the browser.
import { MdAutocomplete } from '@awc-ui/react';
export function Example() {
return <MdAutocomplete></MdAutocomplete>;
}
// ─── Option B: single import (tree-shake to only md-autocomplete) ───
// Skip the wrapper and use the raw custom element. Smallest bundle,
// but you lose typed props/events on JSX.
import '@awc-ui/core/components/md-autocomplete';
export function ExampleTreeShaken() {
return <md-autocomplete></md-autocomplete>;
}// ─── Option A: schema module (any md-* element accepted) ───
// Pair with `defineCustomElements(window)` in main.ts.
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@Component({
standalone: true,
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `<md-autocomplete></md-autocomplete>`,
})
export class ExampleComponent {}
// ─── Option B: typed directive (tree-shake friendly) ───
// Pair with `import '@awc-ui/core/components/md-autocomplete'` in main.ts.
import { Component } from '@angular/core';
import { MdAutocomplete } from '@awc-ui/angular';
@Component({
standalone: true,
imports: [MdAutocomplete],
template: `<md-autocomplete></md-autocomplete>`,
})
export class ExampleTreeShakenComponent {}<!-- ─── Option A: typed Vue wrapper (registers all components) ─── -->
<script setup lang="ts">
import { MdAutocomplete } from '@awc-ui/vue';
</script>
<template>
<MdAutocomplete></MdAutocomplete>
</template>
<!-- ─── Option B: single import (tree-shake to only md-autocomplete) ─── -->
<script setup lang="ts">
import '@awc-ui/core/components/md-autocomplete';
</script>
<template>
<md-autocomplete></md-autocomplete>
</template><!-- ─── Option A: global registration (done once in main entry) ─── -->
<!-- main.ts: -->
<!-- import { defineCustomElements } from '@awc-ui/svelte'; -->
<!-- defineCustomElements(window); -->
<md-autocomplete></md-autocomplete>
<!-- ─── Option B: single import (tree-shake to only md-autocomplete) ─── -->
<script lang="ts">
import '@awc-ui/core/components/md-autocomplete';
</script>
<md-autocomplete></md-autocomplete>free-solo).multiple).| Situation | Use instead |
|---|---|
| A short, closed list | md-select |
| Several values from a known closed list | md-multi-select |
| App-wide search with a results surface | md-search |
| Plain text with no suggestions | md-text-field |
| 2–5 exclusive options | md-segmented-button / md-radio |
| Assigning a subset from a pool | md-transfer-list |
Three sources, and slotted options win:
md-select-option children — best for
a static list you can write in markup.options property — an array of
{ value, label, supportingText?, icon?, iconColor?, disabled? }, for data
that comes from your state layer.options attribute — the same array as a JSON string, for a page
that has its data up front and no reason to reach for a script.<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-autocomplete
label="City"
placeholder="Search cities…"
style="inline-size: 300px;"
options='[
{"value":"lis","label":"Lisbon","supportingText":"Portugal"},
{"value":"osl","label":"Oslo","supportingText":"Norway"},
{"value":"kyo","label":"Kyoto","supportingText":"Japan"},
{"value":"qro","label":"Querétaro","supportingText":"Mexico"}
]'
></md-autocomplete>import { MdAutocomplete } from '@awc-ui/react';
const CITIES = [
{ value: 'lis', label: 'Lisbon', supportingText: 'Portugal' },
{ value: 'osl', label: 'Oslo', supportingText: 'Norway' },
{ value: 'kyo', label: 'Kyoto', supportingText: 'Japan' },
{ value: 'qro', label: 'Querétaro', supportingText: 'Mexico' },
];
export function CityPicker() {
// The wrapper forwards `options` as a property, so pass the array itself —
// the JSON-string form is only needed where you can write attributes alone.
return <MdAutocomplete label="City" placeholder="Search cities…" options={CITIES} />;
}import { Component } from '@angular/core';
@Component({
selector: 'app-city-picker',
template: `
<md-autocomplete label="City" placeholder="Search cities…" [options]="cities"></md-autocomplete>
`,
})
export class CityPickerComponent {
cities = [
{ value: 'lis', label: 'Lisbon', supportingText: 'Portugal' },
{ value: 'osl', label: 'Oslo', supportingText: 'Norway' },
{ value: 'kyo', label: 'Kyoto', supportingText: 'Japan' },
{ value: 'qro', label: 'Querétaro', supportingText: 'Mexico' },
];
}<script setup lang="ts">
import '@awc-ui/core/define';
const cities = [
{ value: 'lis', label: 'Lisbon', supportingText: 'Portugal' },
{ value: 'osl', label: 'Oslo', supportingText: 'Norway' },
{ value: 'kyo', label: 'Kyoto', supportingText: 'Japan' },
{ value: 'qro', label: 'Querétaro', supportingText: 'Mexico' },
];
</script>
<template>
<md-autocomplete label="City" placeholder="Search cities…" :options="cities" />
</template><script lang="ts">
import '@awc-ui/core/define';
const cities = [
{ value: 'lis', label: 'Lisbon', supportingText: 'Portugal' },
{ value: 'osl', label: 'Oslo', supportingText: 'Norway' },
{ value: 'kyo', label: 'Kyoto', supportingText: 'Japan' },
{ value: 'qro', label: 'Querétaro', supportingText: 'Mexico' },
];
</script>
<md-autocomplete label="City" placeholder="Search cities…" options={cities}></md-autocomplete><!-- index.html <head> — the icon font the components draw from -->
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&display=swap">
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-autocomplete label="Assignee" placeholder="Search people…" style="inline-size: 300px;">
<md-select-option value="ada" icon="person" supporting-text="Engineering">Ada Lovelace</md-select-option>
<md-select-option value="grace" icon="person" supporting-text="Engineering">Grace Hopper</md-select-option>
<md-select-option value="alan" icon="person" supporting-text="Research">Alan Turing</md-select-option>
<md-select-option value="katherine" icon="person" supporting-text="Research">Katherine Johnson</md-select-option>
<md-select-option value="linus" icon="person" disabled supporting-text="On leave">Linus Torvalds</md-select-option>
</md-autocomplete>// Icons need the Material Symbols stylesheet in index.html — see Installation.
import { MdAutocomplete, MdSelectOption } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdAutocomplete label="Assignee" placeholder="Search people…" style={{ inlineSize: '300px' }}>
<MdSelectOption value="ada" icon="person" supportingText="Engineering">Ada Lovelace</MdSelectOption>
<MdSelectOption value="grace" icon="person" supportingText="Engineering">Grace Hopper</MdSelectOption>
<MdSelectOption value="alan" icon="person" supportingText="Research">Alan Turing</MdSelectOption>
<MdSelectOption value="katherine" icon="person" supportingText="Research">Katherine Johnson</MdSelectOption>
<MdSelectOption value="linus" icon="person" disabled supportingText="On leave">Linus Torvalds</MdSelectOption>
</MdAutocomplete>
</>
);
}// Icons need the Material Symbols stylesheet in index.html — see Installation.
// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-autocomplete label="Assignee" placeholder="Search people…" style="inline-size: 300px;">
<md-select-option value="ada" icon="person" supporting-text="Engineering">Ada Lovelace</md-select-option>
<md-select-option value="grace" icon="person" supporting-text="Engineering">Grace Hopper</md-select-option>
<md-select-option value="alan" icon="person" supporting-text="Research">Alan Turing</md-select-option>
<md-select-option value="katherine" icon="person" supporting-text="Research">Katherine Johnson</md-select-option>
<md-select-option value="linus" icon="person" disabled supporting-text="On leave">Linus Torvalds</md-select-option>
</md-autocomplete><script setup>
// Icons need the Material Symbols stylesheet in index.html — see Installation.
import '@awc-ui/core/define';
</script>
<template>
<md-autocomplete label="Assignee" placeholder="Search people…" style="inline-size: 300px;">
<md-select-option value="ada" icon="person" supporting-text="Engineering">Ada Lovelace</md-select-option>
<md-select-option value="grace" icon="person" supporting-text="Engineering">Grace Hopper</md-select-option>
<md-select-option value="alan" icon="person" supporting-text="Research">Alan Turing</md-select-option>
<md-select-option value="katherine" icon="person" supporting-text="Research">Katherine Johnson</md-select-option>
<md-select-option value="linus" icon="person" disabled supporting-text="On leave">Linus Torvalds</md-select-option>
</md-autocomplete>
</template><script>
// Icons need the Material Symbols stylesheet in index.html — see Installation.
import '@awc-ui/core/define';
</script>
<md-autocomplete label="Assignee" placeholder="Search people…" style="inline-size: 300px;">
<md-select-option value="ada" icon="person" supporting-text="Engineering">Ada Lovelace</md-select-option>
<md-select-option value="grace" icon="person" supporting-text="Engineering">Grace Hopper</md-select-option>
<md-select-option value="alan" icon="person" supporting-text="Research">Alan Turing</md-select-option>
<md-select-option value="katherine" icon="person" supporting-text="Research">Katherine Johnson</md-select-option>
<md-select-option value="linus" icon="person" disabled supporting-text="On leave">Linus Torvalds</md-select-option>
</md-autocomplete>value vs inputValueThe single most common mix-up on this component.
| Holds | Reported by | |
|---|---|---|
value | The committed selection — a string, or string[] when multiple | mdChange |
inputValue | The raw text in the box | mdInput |
Reading value to get what the user typed returns the last selection, not the
text. Reading mdInput as a selection fires on every keystroke.
filled is the default — the opposite of
md-select, whose default is outlined.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-autocomplete variant="filled" label="Filled" style="inline-size: 240px;">
<md-select-option value="a">Alpha</md-select-option>
<md-select-option value="b">Bravo</md-select-option>
<md-select-option value="c">Charlie</md-select-option>
</md-autocomplete>
<md-autocomplete variant="outlined" label="Outlined" style="inline-size: 240px;">
<md-select-option value="a">Alpha</md-select-option>
<md-select-option value="b">Bravo</md-select-option>
<md-select-option value="c">Charlie</md-select-option>
</md-autocomplete>import { MdAutocomplete, MdSelectOption } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdAutocomplete variant="filled" label="Filled" style={{ inlineSize: '240px' }}>
<MdSelectOption value="a">Alpha</MdSelectOption>
<MdSelectOption value="b">Bravo</MdSelectOption>
<MdSelectOption value="c">Charlie</MdSelectOption>
</MdAutocomplete>
<MdAutocomplete variant="outlined" label="Outlined" style={{ inlineSize: '240px' }}>
<MdSelectOption value="a">Alpha</MdSelectOption>
<MdSelectOption value="b">Bravo</MdSelectOption>
<MdSelectOption value="c">Charlie</MdSelectOption>
</MdAutocomplete>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-autocomplete variant="filled" label="Filled" style="inline-size: 240px;">
<md-select-option value="a">Alpha</md-select-option>
<md-select-option value="b">Bravo</md-select-option>
<md-select-option value="c">Charlie</md-select-option>
</md-autocomplete>
<md-autocomplete variant="outlined" label="Outlined" style="inline-size: 240px;">
<md-select-option value="a">Alpha</md-select-option>
<md-select-option value="b">Bravo</md-select-option>
<md-select-option value="c">Charlie</md-select-option>
</md-autocomplete><script setup>
import '@awc-ui/core/define';
</script>
<template>
<md-autocomplete variant="filled" label="Filled" style="inline-size: 240px;">
<md-select-option value="a">Alpha</md-select-option>
<md-select-option value="b">Bravo</md-select-option>
<md-select-option value="c">Charlie</md-select-option>
</md-autocomplete>
<md-autocomplete variant="outlined" label="Outlined" style="inline-size: 240px;">
<md-select-option value="a">Alpha</md-select-option>
<md-select-option value="b">Bravo</md-select-option>
<md-select-option value="c">Charlie</md-select-option>
</md-autocomplete>
</template><script>
import '@awc-ui/core/define';
</script>
<md-autocomplete variant="filled" label="Filled" style="inline-size: 240px;">
<md-select-option value="a">Alpha</md-select-option>
<md-select-option value="b">Bravo</md-select-option>
<md-select-option value="c">Charlie</md-select-option>
</md-autocomplete>
<md-autocomplete variant="outlined" label="Outlined" style="inline-size: 240px;">
<md-select-option value="a">Alpha</md-select-option>
<md-select-option value="b">Bravo</md-select-option>
<md-select-option value="c">Charlie</md-select-option>
</md-autocomplete>multiple turns value into a string[] and renders each pick as a chip.
chip-position places the chip rail — below (default), top, left,
right or inline. A multi-select menu already stays open after a pick, so
several can be made in a row; disable-close-on-select is for single mode,
and it also makes the popup persistent — no outside-click dismissal, only
Escape, the trigger or a pick closes it.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-autocomplete multiple label="Tags" placeholder="Add tags…" style="inline-size: 320px;">
<md-select-option value="design">Design</md-select-option>
<md-select-option value="eng">Engineering</md-select-option>
<md-select-option value="docs">Documentation</md-select-option>
<md-select-option value="a11y">Accessibility</md-select-option>
<md-select-option value="perf">Performance</md-select-option>
</md-autocomplete>
<md-autocomplete multiple chip-position="inline" label="Inline chips" placeholder="Add tags…" style="inline-size: 320px;">
<md-select-option value="design">Design</md-select-option>
<md-select-option value="eng">Engineering</md-select-option>
<md-select-option value="docs">Documentation</md-select-option>
</md-autocomplete>import { MdAutocomplete, MdSelectOption } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdAutocomplete multiple label="Tags" placeholder="Add tags…" style={{ inlineSize: '320px' }}>
<MdSelectOption value="design">Design</MdSelectOption>
<MdSelectOption value="eng">Engineering</MdSelectOption>
<MdSelectOption value="docs">Documentation</MdSelectOption>
<MdSelectOption value="a11y">Accessibility</MdSelectOption>
<MdSelectOption value="perf">Performance</MdSelectOption>
</MdAutocomplete>
<MdAutocomplete multiple chipPosition="inline" label="Inline chips" placeholder="Add tags…" style={{ inlineSize: '320px' }}>
<MdSelectOption value="design">Design</MdSelectOption>
<MdSelectOption value="eng">Engineering</MdSelectOption>
<MdSelectOption value="docs">Documentation</MdSelectOption>
</MdAutocomplete>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-autocomplete multiple label="Tags" placeholder="Add tags…" style="inline-size: 320px;">
<md-select-option value="design">Design</md-select-option>
<md-select-option value="eng">Engineering</md-select-option>
<md-select-option value="docs">Documentation</md-select-option>
<md-select-option value="a11y">Accessibility</md-select-option>
<md-select-option value="perf">Performance</md-select-option>
</md-autocomplete>
<md-autocomplete multiple chip-position="inline" label="Inline chips" placeholder="Add tags…" style="inline-size: 320px;">
<md-select-option value="design">Design</md-select-option>
<md-select-option value="eng">Engineering</md-select-option>
<md-select-option value="docs">Documentation</md-select-option>
</md-autocomplete><script setup>
import '@awc-ui/core/define';
</script>
<template>
<md-autocomplete multiple label="Tags" placeholder="Add tags…" style="inline-size: 320px;">
<md-select-option value="design">Design</md-select-option>
<md-select-option value="eng">Engineering</md-select-option>
<md-select-option value="docs">Documentation</md-select-option>
<md-select-option value="a11y">Accessibility</md-select-option>
<md-select-option value="perf">Performance</md-select-option>
</md-autocomplete>
<md-autocomplete multiple chip-position="inline" label="Inline chips" placeholder="Add tags…" style="inline-size: 320px;">
<md-select-option value="design">Design</md-select-option>
<md-select-option value="eng">Engineering</md-select-option>
<md-select-option value="docs">Documentation</md-select-option>
</md-autocomplete>
</template><script>
import '@awc-ui/core/define';
</script>
<md-autocomplete multiple label="Tags" placeholder="Add tags…" style="inline-size: 320px;">
<md-select-option value="design">Design</md-select-option>
<md-select-option value="eng">Engineering</md-select-option>
<md-select-option value="docs">Documentation</md-select-option>
<md-select-option value="a11y">Accessibility</md-select-option>
<md-select-option value="perf">Performance</md-select-option>
</md-autocomplete>
<md-autocomplete multiple chip-position="inline" label="Inline chips" placeholder="Add tags…" style="inline-size: 320px;">
<md-select-option value="design">Design</md-select-option>
<md-select-option value="eng">Engineering</md-select-option>
<md-select-option value="docs">Documentation</md-select-option>
</md-autocomplete>chip-position="left" / "right" are physical, not logical — re-check them
in RTL.
free-solo lets value hold text that isn’t in options. The component
accepts anything — validating it is yours.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-autocomplete free-solo label="Tag" placeholder="Pick one or invent one" style="inline-size: 300px;">
<md-select-option value="bug">bug</md-select-option>
<md-select-option value="feature">feature</md-select-option>
<md-select-option value="chore">chore</md-select-option>
</md-autocomplete>import { MdAutocomplete, MdSelectOption } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdAutocomplete freeSolo label="Tag" placeholder="Pick one or invent one" style={{ inlineSize: '300px' }}>
<MdSelectOption value="bug">bug</MdSelectOption>
<MdSelectOption value="feature">feature</MdSelectOption>
<MdSelectOption value="chore">chore</MdSelectOption>
</MdAutocomplete>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-autocomplete free-solo label="Tag" placeholder="Pick one or invent one" style="inline-size: 300px;">
<md-select-option value="bug">bug</md-select-option>
<md-select-option value="feature">feature</md-select-option>
<md-select-option value="chore">chore</md-select-option>
</md-autocomplete><script setup>
import '@awc-ui/core/define';
</script>
<template>
<md-autocomplete free-solo label="Tag" placeholder="Pick one or invent one" style="inline-size: 300px;">
<md-select-option value="bug">bug</md-select-option>
<md-select-option value="feature">feature</md-select-option>
<md-select-option value="chore">chore</md-select-option>
</md-autocomplete>
</template><script>
import '@awc-ui/core/define';
</script>
<md-autocomplete free-solo label="Tag" placeholder="Pick one or invent one" style="inline-size: 300px;">
<md-select-option value="bug">bug</md-select-option>
<md-select-option value="feature">feature</md-select-option>
<md-select-option value="chore">chore</md-select-option>
</md-autocomplete><!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-autocomplete id="tag" free-solo label="Tag">
<md-select-option value="feat">feat</md-select-option>
<md-select-option value="fix">fix</md-select-option>
<md-select-option value="chore">chore</md-select-option>
</md-autocomplete>
<script type="module">
const el = document.getElementById('tag');
const known = new Set(['feat', 'fix', 'chore']);
el.addEventListener('mdChange', (e) => {
el.setCustomValidity(known.has(e.detail) ? '' : 'Unknown tag');
});
</script>import { MdAutocomplete, MdSelectOption } from '@awc-ui/react';
const KNOWN = new Set(['feat', 'fix', 'chore']);
export function TagField() {
// The event carries the element, so the handler is a prop — no ref, no effect.
return (
<MdAutocomplete
freeSolo
label="Tag"
onMdChange={(e) => {
const el = e.target as HTMLElement & { setCustomValidity(m: string): void };
el.setCustomValidity(KNOWN.has(e.detail) ? '' : 'Unknown tag');
}}
>
<MdSelectOption value="feat">feat</MdSelectOption>
<MdSelectOption value="fix">fix</MdSelectOption>
<MdSelectOption value="chore">chore</MdSelectOption>
</MdAutocomplete>
);
}import { Component } from '@angular/core';
@Component({
selector: 'app-tag-field',
template: `
<md-autocomplete free-solo label="Tag" (mdChange)="validate($event)">
<md-select-option value="feat">feat</md-select-option>
<md-select-option value="fix">fix</md-select-option>
<md-select-option value="chore">chore</md-select-option>
</md-autocomplete>
`,
})
export class TagFieldComponent {
private known = new Set(['feat', 'fix', 'chore']);
validate(event: CustomEvent<string>) {
const el = event.target as HTMLElement & { setCustomValidity(m: string): void };
el.setCustomValidity(this.known.has(event.detail) ? '' : 'Unknown tag');
}
}<script setup lang="ts">
import '@awc-ui/core/define';
const known = new Set(['feat', 'fix', 'chore']);
function validate(event: CustomEvent<string>) {
const el = event.target as HTMLElement & { setCustomValidity(m: string): void };
el.setCustomValidity(known.has(event.detail) ? '' : 'Unknown tag');
}
</script>
<template>
<md-autocomplete free-solo label="Tag" @mdChange="validate">
<md-select-option value="feat">feat</md-select-option>
<md-select-option value="fix">fix</md-select-option>
<md-select-option value="chore">chore</md-select-option>
</md-autocomplete>
</template><script lang="ts">
import '@awc-ui/core/define';
const known = new Set(['feat', 'fix', 'chore']);
const validate = (event: CustomEvent<string>) => {
const el = event.target as HTMLElement & { setCustomValidity(m: string): void };
el.setCustomValidity(known.has(event.detail) ? '' : 'Unknown tag');
};
</script>
<md-autocomplete free-solo label="Tag" on:mdChange={validate}>
<md-select-option value="feat">feat</md-select-option>
<md-select-option value="fix">fix</md-select-option>
<md-select-option value="chore">chore</md-select-option>
</md-autocomplete>clear-on-blur discards unmatched text when the field loses focus — strict
single mode only: it is ignored when multiple or free-solo is set, and it
never fires while the popup is open (arrowing into the listbox must not wipe the
active filter).
Three distinct messages, and they are not interchangeable:
| Prop | Shown when |
|---|---|
loading-text | loading is true |
no-options-text | There are no options at all |
no-results-text | There are options, but nothing matched the query |
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-autocomplete loading loading-text="Fetching cities…" label="Loading" placeholder="Click to open" style="inline-size: 260px;"></md-autocomplete>
<md-autocomplete no-options-text="No cities configured" label="No options" placeholder="Click to open" style="inline-size: 260px;"></md-autocomplete>
<md-autocomplete no-results-text="No city matches that" label="No results" placeholder="Type zzz" style="inline-size: 260px;">
<md-select-option value="par">Paris</md-select-option>
<md-select-option value="ber">Berlin</md-select-option>
</md-autocomplete>import { MdAutocomplete, MdSelectOption } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdAutocomplete loading loadingText="Fetching cities…" label="Loading" placeholder="Click to open" style={{ inlineSize: '260px' }}></MdAutocomplete>
<MdAutocomplete noOptionsText="No cities configured" label="No options" placeholder="Click to open" style={{ inlineSize: '260px' }}></MdAutocomplete>
<MdAutocomplete noResultsText="No city matches that" label="No results" placeholder="Type zzz" style={{ inlineSize: '260px' }}>
<MdSelectOption value="par">Paris</MdSelectOption>
<MdSelectOption value="ber">Berlin</MdSelectOption>
</MdAutocomplete>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-autocomplete loading loading-text="Fetching cities…" label="Loading" placeholder="Click to open" style="inline-size: 260px;"></md-autocomplete>
<md-autocomplete no-options-text="No cities configured" label="No options" placeholder="Click to open" style="inline-size: 260px;"></md-autocomplete>
<md-autocomplete no-results-text="No city matches that" label="No results" placeholder="Type zzz" style="inline-size: 260px;">
<md-select-option value="par">Paris</md-select-option>
<md-select-option value="ber">Berlin</md-select-option>
</md-autocomplete><script setup>
import '@awc-ui/core/define';
</script>
<template>
<md-autocomplete loading loading-text="Fetching cities…" label="Loading" placeholder="Click to open" style="inline-size: 260px;"></md-autocomplete>
<md-autocomplete no-options-text="No cities configured" label="No options" placeholder="Click to open" style="inline-size: 260px;"></md-autocomplete>
<md-autocomplete no-results-text="No city matches that" label="No results" placeholder="Type zzz" style="inline-size: 260px;">
<md-select-option value="par">Paris</md-select-option>
<md-select-option value="ber">Berlin</md-select-option>
</md-autocomplete>
</template><script>
import '@awc-ui/core/define';
</script>
<md-autocomplete loading loading-text="Fetching cities…" label="Loading" placeholder="Click to open" style="inline-size: 260px;"></md-autocomplete>
<md-autocomplete no-options-text="No cities configured" label="No options" placeholder="Click to open" style="inline-size: 260px;"></md-autocomplete>
<md-autocomplete no-results-text="No city matches that" label="No results" placeholder="Type zzz" style="inline-size: 260px;">
<md-select-option value="par">Paris</md-select-option>
<md-select-option value="ber">Berlin</md-select-option>
</md-autocomplete>Slot your own into loader when the default spinner isn’t the right affordance
— a circular md-progress-indicator, a
branded mark, a skeleton. Everything else about the loading state is unchanged.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-autocomplete loading loading-text="Fetching…" label="Default loader" style="inline-size: 260px;"></md-autocomplete>
<md-autocomplete loading loading-text="Fetching…" label="Slotted loader" style="inline-size: 260px;">
<md-progress-indicator slot="loader" variant="circular" indeterminate size="24" label="Loading"></md-progress-indicator>
</md-autocomplete>import { MdAutocomplete, MdProgressIndicator } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdAutocomplete loading loadingText="Fetching…" label="Default loader" style={{ inlineSize: '260px' }}></MdAutocomplete>
<MdAutocomplete loading loadingText="Fetching…" label="Slotted loader" style={{ inlineSize: '260px' }}>
<MdProgressIndicator slot="loader" variant="circular" indeterminate size="24" label="Loading"></MdProgressIndicator>
</MdAutocomplete>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-autocomplete loading loading-text="Fetching…" label="Default loader" style="inline-size: 260px;"></md-autocomplete>
<md-autocomplete loading loading-text="Fetching…" label="Slotted loader" style="inline-size: 260px;">
<md-progress-indicator slot="loader" variant="circular" indeterminate size="24" label="Loading"></md-progress-indicator>
</md-autocomplete><script setup>
import '@awc-ui/core/define';
</script>
<template>
<md-autocomplete loading loading-text="Fetching…" label="Default loader" style="inline-size: 260px;"></md-autocomplete>
<md-autocomplete loading loading-text="Fetching…" label="Slotted loader" style="inline-size: 260px;">
<md-progress-indicator slot="loader" variant="circular" indeterminate size="24" label="Loading"></md-progress-indicator>
</md-autocomplete>
</template><script>
import '@awc-ui/core/define';
</script>
<md-autocomplete loading loading-text="Fetching…" label="Default loader" style="inline-size: 260px;"></md-autocomplete>
<md-autocomplete loading loading-text="Fetching…" label="Slotted loader" style="inline-size: 260px;">
<md-progress-indicator slot="loader" variant="circular" indeterminate size="24" label="Loading"></md-progress-indicator>
</md-autocomplete><!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-autocomplete label="Enabled" style="inline-size: 240px;">
<md-select-option value="a">Alpha</md-select-option>
<md-select-option value="b">Bravo</md-select-option>
</md-autocomplete>
<md-autocomplete label="Disabled" disabled style="inline-size: 240px;"></md-autocomplete>
<md-autocomplete label="Soft-disabled" soft-disabled style="inline-size: 240px;"></md-autocomplete>
<md-autocomplete label="Error" error error-text="Pick a city" required style="inline-size: 240px;"></md-autocomplete>
<md-autocomplete label="Supporting text" supporting-text="Start typing to filter" reserve-supporting-space style="inline-size: 240px;">
<md-select-option value="a">Alpha</md-select-option>
</md-autocomplete>import { MdAutocomplete, MdSelectOption } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdAutocomplete label="Enabled" style={{ inlineSize: '240px' }}>
<MdSelectOption value="a">Alpha</MdSelectOption>
<MdSelectOption value="b">Bravo</MdSelectOption>
</MdAutocomplete>
<MdAutocomplete label="Disabled" disabled style={{ inlineSize: '240px' }}></MdAutocomplete>
<MdAutocomplete label="Soft-disabled" softDisabled style={{ inlineSize: '240px' }}></MdAutocomplete>
<MdAutocomplete label="Error" error errorText="Pick a city" required style={{ inlineSize: '240px' }}></MdAutocomplete>
<MdAutocomplete label="Supporting text" supportingText="Start typing to filter" reserveSupportingSpace style={{ inlineSize: '240px' }}>
<MdSelectOption value="a">Alpha</MdSelectOption>
</MdAutocomplete>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-autocomplete label="Enabled" style="inline-size: 240px;">
<md-select-option value="a">Alpha</md-select-option>
<md-select-option value="b">Bravo</md-select-option>
</md-autocomplete>
<md-autocomplete label="Disabled" disabled style="inline-size: 240px;"></md-autocomplete>
<md-autocomplete label="Soft-disabled" soft-disabled style="inline-size: 240px;"></md-autocomplete>
<md-autocomplete label="Error" error error-text="Pick a city" required style="inline-size: 240px;"></md-autocomplete>
<md-autocomplete label="Supporting text" supporting-text="Start typing to filter" reserve-supporting-space style="inline-size: 240px;">
<md-select-option value="a">Alpha</md-select-option>
</md-autocomplete><script setup>
import '@awc-ui/core/define';
</script>
<template>
<md-autocomplete label="Enabled" style="inline-size: 240px;">
<md-select-option value="a">Alpha</md-select-option>
<md-select-option value="b">Bravo</md-select-option>
</md-autocomplete>
<md-autocomplete label="Disabled" disabled style="inline-size: 240px;"></md-autocomplete>
<md-autocomplete label="Soft-disabled" soft-disabled style="inline-size: 240px;"></md-autocomplete>
<md-autocomplete label="Error" error error-text="Pick a city" required style="inline-size: 240px;"></md-autocomplete>
<md-autocomplete label="Supporting text" supporting-text="Start typing to filter" reserve-supporting-space style="inline-size: 240px;">
<md-select-option value="a">Alpha</md-select-option>
</md-autocomplete>
</template><script>
import '@awc-ui/core/define';
</script>
<md-autocomplete label="Enabled" style="inline-size: 240px;">
<md-select-option value="a">Alpha</md-select-option>
<md-select-option value="b">Bravo</md-select-option>
</md-autocomplete>
<md-autocomplete label="Disabled" disabled style="inline-size: 240px;"></md-autocomplete>
<md-autocomplete label="Soft-disabled" soft-disabled style="inline-size: 240px;"></md-autocomplete>
<md-autocomplete label="Error" error error-text="Pick a city" required style="inline-size: 240px;"></md-autocomplete>
<md-autocomplete label="Supporting text" supporting-text="Start typing to filter" reserve-supporting-space style="inline-size: 240px;">
<md-select-option value="a">Alpha</md-select-option>
</md-autocomplete>reserve-supporting-space holds the line height so the layout doesn’t jump
when an error appears. Show supporting text or error text — never both.
filter-mode picks a built-in matching strategy (substring by default).
filterer replaces matching entirely and is a function property:
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-autocomplete id="country" label="Country"></md-autocomplete>
<script type="module">
const el = document.getElementById('country');
// Function property — there is no filterer attribute.
el.filterer = (options, { inputValue }) =>
options.filter((o) => o.label.toLowerCase().startsWith(inputValue.toLowerCase()));
</script>import { MdAutocomplete } from '@awc-ui/react';
// Function property — there is no filterer attribute.
const filterer = (options, { inputValue }) =>
options.filter((o) => o.label.toLowerCase().startsWith(inputValue.toLowerCase()));
export function Demo() {
return (
<>
<MdAutocomplete id="country"
filterer={filterer} label="Country"></MdAutocomplete>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
// app.component.ts — the bound values live on the class
import { Component } from '@angular/core';
@Component({
selector: 'app-demo',
templateUrl: './app.component.html',
})
export class DemoComponent {
filterer = (options, { inputValue }) =>
options.filter((o) => o.label.toLowerCase().startsWith(inputValue.toLowerCase()));
}
<!-- app.component.html -->
<md-autocomplete id="country"
[filterer]="filterer" label="Country"></md-autocomplete><script setup>
import '@awc-ui/core/define';
const filterer = (options, { inputValue }) =>
options.filter((o) => o.label.toLowerCase().startsWith(inputValue.toLowerCase()));
</script>
<template>
<md-autocomplete id="country"
:filterer="filterer" label="Country"></md-autocomplete>
</template><script>
import '@awc-ui/core/define';
const filterer = (options, { inputValue }) =>
options.filter((o) => o.label.toLowerCase().startsWith(inputValue.toLowerCase()));
</script>
<md-autocomplete id="country"
{filterer} label="Country"></md-autocomplete>limit-results caps how many suggestions render on the client-side path —
a rendering guard, not a search limit, since matching still scans every option.
The virtualized path ignores it: the WASM listbox windows its own rows.
virtualize="always" renders rows on demand; pair it with row-height so the
scroller can size itself. virtualize="auto" (the default) decides for you.
For a dataset that big, don’t hand it an array — options would mean
materialising every row on the JS heap. loadOptions({ count, getRow }) takes a
row factory and packs the rows into the WASM store one at a time, so the data
never exists in JS. Filtering and scrolling then run against the packed store.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-autocomplete
id="options"
label="Option"
virtualize="always"
row-height="48"
></md-autocomplete>
<script type="module">
const el = document.getElementById('options');
// A row FACTORY, not an array: loadOptions packs the dataset into the WASM
// store one row at a time, so the rows never exist on the JS heap.
// Keep the factory cheap — it runs once per row. Formatting each label with
// toLocaleString() added 3.4s to a 10M pack in this very demo.
await el.loadOptions({
count: 10_000_000,
getRow: (i) => ({ value: 'v' + i, label: 'Option ' + i }),
});
</script>import { useEffect, useRef } from 'react';
import { MdAutocomplete } from '@awc-ui/react';
export function Demo() {
const elRef = useRef(null);
useEffect(() => {
const el = elRef.current;
// A row FACTORY, not an array: loadOptions packs the dataset into the WASM
// store one row at a time, so the rows never exist on the JS heap.
// Keep the factory cheap — it runs once per row. Formatting each label with
// toLocaleString() added 3.4s to a 10M pack in this very demo.
await el.loadOptions({
count: 10_000_000,
getRow: (i) => ({ value: 'v' + i, label: 'Option ' + i }),
});
}, []);
return (
<>
<MdAutocomplete
id="options" ref={elRef}
label="Option"
virtualize="always"
rowHeight="48"
></MdAutocomplete>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
// app.component.ts
import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core';
@Component({
selector: 'app-demo',
templateUrl: './app.component.html',
})
export class DemoComponent implements AfterViewInit {
@ViewChild('el') elRef!: ElementRef;
ngAfterViewInit() {
const el = this.elRef.nativeElement;
// A row FACTORY, not an array: loadOptions packs the dataset into the WASM
// store one row at a time, so the rows never exist on the JS heap.
// Keep the factory cheap — it runs once per row. Formatting each label with
// toLocaleString() added 3.4s to a 10M pack in this very demo.
await el.loadOptions({
count: 10_000_000,
getRow: (i) => ({ value: 'v' + i, label: 'Option ' + i }),
});
}
}
<!-- app.component.html -->
<md-autocomplete
id="options" #el
label="Option"
virtualize="always"
row-height="48"
></md-autocomplete><script setup>
import { onMounted, ref } from 'vue';
import '@awc-ui/core/define';
const elRef = ref(null);
onMounted(() => {
const el = elRef.value;
// A row FACTORY, not an array: loadOptions packs the dataset into the WASM
// store one row at a time, so the rows never exist on the JS heap.
// Keep the factory cheap — it runs once per row. Formatting each label with
// toLocaleString() added 3.4s to a 10M pack in this very demo.
await el.loadOptions({
count: 10_000_000,
getRow: (i) => ({ value: 'v' + i, label: 'Option ' + i }),
});
});
</script>
<template>
<md-autocomplete
id="options" ref="elRef"
label="Option"
virtualize="always"
row-height="48"
></md-autocomplete>
</template><script>
import { onMount } from 'svelte';
import '@awc-ui/core/define';
let elRef;
onMount(() => {
const el = elRef;
// A row FACTORY, not an array: loadOptions packs the dataset into the WASM
// store one row at a time, so the rows never exist on the JS heap.
// Keep the factory cheap — it runs once per row. Formatting each label with
// toLocaleString() added 3.4s to a 10M pack in this very demo.
await el.loadOptions({
count: 10_000_000,
getRow: (i) => ({ value: 'v' + i, label: 'Option ' + i }),
});
});
</script>
<md-autocomplete
id="options" bind:this={elRef}
label="Option"
virtualize="always"
row-height="48"
></md-autocomplete>| Event | Cancelable | Detail | Fires |
|---|---|---|---|
mdInput | no | string | Every change to the typed text |
mdChange | no | string | string[] | The committed selection changes |
mdOpen / mdClose | no | void | The suggestion menu opens / closes |
mdClear | no | void | The clear affordance is used |
mdValidityChange | no | { valid, validationMessage, flags } | Validity changes — not composed |
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-autocomplete id="city" label="City" name="city" required></md-autocomplete>
<script type="module">
const el = document.getElementById('city');
el.options = [];
let t;
el.addEventListener('mdInput', (e) => { // e.detail is the TYPED TEXT
clearTimeout(t);
t = setTimeout(async () => {
el.loading = true;
el.options = await searchCities(e.detail);
el.loading = false;
}, 250);
});
el.addEventListener('mdChange', (e) => save(e.detail)); // committed selection
</script>import { MdAutocomplete } from '@awc-ui/react';
import { useState } from 'react';
export function CityField() {
const [options, setOptions] = useState([]);
const [loading, setLoading] = useState(false);
// The wrapper forwards options as a PROPERTY, so the array is just a prop —
// no ref, no effect. It re-forwards on every state change.
return (
<MdAutocomplete
label="City"
name="city"
required
loading={loading}
options={options}
onMdInput={async (e) => {
setLoading(true);
setOptions(await searchCities(e.detail));
setLoading(false);
}}
onMdChange={(e) => save(e.detail)}
/>
);
}import { Component } from '@angular/core';
@Component({
selector: 'app-city-field',
template: `
<md-autocomplete
label="City"
name="city"
required
[loading]="loading"
[options]="options"
(mdInput)="onInput($event)"
(mdChange)="onChange($event)"
></md-autocomplete>
`,
})
export class CityFieldComponent {
// A property binding forwards the array — no ViewChild, no imperative assign.
options: unknown[] = [];
loading = false;
async onInput(e: CustomEvent<string>) {
this.loading = true;
this.options = await searchCities(e.detail);
this.loading = false;
}
onChange(e: CustomEvent<string | string[]>) { save(e.detail); }
}<script setup lang="ts">
import { ref } from 'vue';
// A property binding forwards the array — no template ref, no watcher.
const options = ref<unknown[]>([]);
const loading = ref(false);
async function onInput(e: CustomEvent<string>) {
loading.value = true;
options.value = await searchCities(e.detail);
loading.value = false;
}
</script>
<template>
<md-autocomplete
label="City"
name="city"
required
:loading="loading"
:options="options"
@mdInput="onInput"
@mdChange="(e) => save(e.detail)"
/>
</template><script lang="ts">
// A property binding forwards the array — no bind:this, no imperative assign.
let options: unknown[] = [];
let loading = false;
async function onInput(e: CustomEvent<string>) {
loading = true;
options = await searchCities(e.detail);
loading = false;
}
</script>
<md-autocomplete
label="City"
name="city"
required
{loading}
{options}
on:mdInput={onInput}
on:mdChange={(e) => save(e.detail)}
/>The component is form-associated via ElementInternals, so name and
required participate in FormData and in native constraint validation.
value-missing-label is the message shown when a required field is empty.
getValidity(), checkValidity(), reportValidity() and
setCustomValidity() are available as methods.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<form id="signup">
<md-autocomplete
id="city"
name="city"
label="City"
required
value-missing-label="Pick a city"
>
<md-select-option value="lon">London</md-select-option>
<md-select-option value="par">Paris</md-select-option>
</md-autocomplete>
<md-button variant="filled" type="submit">Save</md-button>
<md-button variant="text" type="reset">Reset</md-button>
</form>
<script type="module">
const form = document.getElementById('signup');
const city = document.getElementById('city');
form.addEventListener('submit', (e) => {
e.preventDefault();
// name/value land in FormData across the shadow boundary — no hidden input.
console.log(Object.fromEntries(new FormData(form))); // { city: 'lon' }
});
// Same validity surface as a native control.
const { valid, validationMessage } = await city.getValidity();
</script>import { useEffect, useRef } from 'react';
import { MdAutocomplete, MdButton, MdSelectOption } from '@awc-ui/react';
export function Demo() {
const formRef = useRef(null);
const cityRef = useRef(null);
useEffect(() => {
const form = formRef.current;
const city = cityRef.current;
form.addEventListener('submit', (e) => {
e.preventDefault();
// name/value land in FormData across the shadow boundary — no hidden input.
console.log(Object.fromEntries(new FormData(form))); // { city: 'lon' }
});
// Same validity surface as a native control.
const { valid, validationMessage } = await city.getValidity();
}, []);
return (
<>
<form id="signup" ref={formRef}>
<MdAutocomplete
id="city" ref={cityRef}
name="city"
label="City"
required
valueMissingLabel="Pick a city"
>
<MdSelectOption value="lon">London</MdSelectOption>
<MdSelectOption value="par">Paris</MdSelectOption>
</MdAutocomplete>
<MdButton variant="filled" type="submit">Save</MdButton>
<MdButton variant="text" type="reset">Reset</MdButton>
</form>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
// app.component.ts
import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core';
@Component({
selector: 'app-demo',
templateUrl: './app.component.html',
})
export class DemoComponent implements AfterViewInit {
@ViewChild('form') formRef!: ElementRef;
@ViewChild('city') cityRef!: ElementRef;
ngAfterViewInit() {
const form = this.formRef.nativeElement;
const city = this.cityRef.nativeElement;
form.addEventListener('submit', (e) => {
e.preventDefault();
// name/value land in FormData across the shadow boundary — no hidden input.
console.log(Object.fromEntries(new FormData(form))); // { city: 'lon' }
});
// Same validity surface as a native control.
const { valid, validationMessage } = await city.getValidity();
}
}
<!-- app.component.html -->
<form id="signup" #form>
<md-autocomplete
id="city" #city
name="city"
label="City"
required
value-missing-label="Pick a city"
>
<md-select-option value="lon">London</md-select-option>
<md-select-option value="par">Paris</md-select-option>
</md-autocomplete>
<md-button variant="filled" type="submit">Save</md-button>
<md-button variant="text" type="reset">Reset</md-button>
</form><script setup>
import { onMounted, ref } from 'vue';
import '@awc-ui/core/define';
const formRef = ref(null);
const cityRef = ref(null);
onMounted(() => {
const form = formRef.value;
const city = cityRef.value;
form.addEventListener('submit', (e) => {
e.preventDefault();
// name/value land in FormData across the shadow boundary — no hidden input.
console.log(Object.fromEntries(new FormData(form))); // { city: 'lon' }
});
// Same validity surface as a native control.
const { valid, validationMessage } = await city.getValidity();
});
</script>
<template>
<form id="signup" ref="formRef">
<md-autocomplete
id="city" ref="cityRef"
name="city"
label="City"
required
value-missing-label="Pick a city"
>
<md-select-option value="lon">London</md-select-option>
<md-select-option value="par">Paris</md-select-option>
</md-autocomplete>
<md-button variant="filled" type="submit">Save</md-button>
<md-button variant="text" type="reset">Reset</md-button>
</form>
</template><script>
import { onMount } from 'svelte';
import '@awc-ui/core/define';
let formRef;
let cityRef;
onMount(() => {
const form = formRef;
const city = cityRef;
form.addEventListener('submit', (e) => {
e.preventDefault();
// name/value land in FormData across the shadow boundary — no hidden input.
console.log(Object.fromEntries(new FormData(form))); // { city: 'lon' }
});
// Same validity surface as a native control.
const { valid, validationMessage } = await city.getValidity();
});
</script>
<form id="signup" bind:this={formRef}>
<md-autocomplete
id="city" bind:this={cityRef}
name="city"
label="City"
required
value-missing-label="Pick a city"
>
<md-select-option value="lon">London</md-select-option>
<md-select-option value="par">Paris</md-select-option>
</md-autocomplete>
<md-button variant="filled" type="submit">Save</md-button>
<md-button variant="text" type="reset">Reset</md-button>
</form>| Property | Attribute | Type | Default | Reflects |
|---|---|---|---|---|
valueMissingLabel | value-missing-label | string | 'Please make a selection' | — |
reserveSupportingSpace | reserve-supporting-space | boolean | false | — |
variant | variant | 'filled' | 'outlined' | 'filled' | Yes |
label | label | string | '' | — |
placeholder | placeholder | string | '' | — |
supportingText | supporting-text | string | '' | — |
error | error | boolean | false | Yes |
errorText | error-text | string | '' | — |
disabled | disabled | boolean | false | Yes |
softDisabled | soft-disabled | boolean | false | Yes |
required | required | boolean | false | Yes |
density | density | 0 | -1 | -2 | -3 | -4 | 0 | Yes |
options | options | MdAutocompleteOption[] | string | [] | — |
multiple | multiple | boolean | false | Yes |
chipPosition | chip-position | | 'below' | 'top' | 'left' | 'right' | 'inline' | 'below' | Yes |
value | value | string | string[] | '' | — |
inputValue | input-value | string | '' | — |
freeSolo | free-solo | boolean | false | Yes |
maxSelected | max-selected | number | 0 | — |
loading | loading | boolean | false | Yes |
noOptionsText | no-options-text | string | 'No options' | — |
noResultsText | no-results-text | string | 'No results' | — |
loadingText | loading-text | string | 'Loading…' | — |
statusTemplate | status-template | string | '{count} suggestions available' | — |
disableCloseOnSelect | disable-close-on-select | boolean | false | Yes |
clearOnBlur | clear-on-blur | boolean | false | Yes |
clearable | clearable | boolean | true | Yes |
clearIcon | clear-icon | string | 'close' | — |
dropdownIcon | dropdown-icon | string | 'arrow_drop_down' | — |
limitResults | limit-results | number | 0 | — |
open | open | boolean | false | Yes |
filterer | JS only | ( options: SelectOptionData[], state: { inputValue: string | — | — |
virtualize | virtualize | 'auto' | 'always' | 'never' | 'auto' | — |
filterMode | filter-mode | FilterMode | 'substring' | — |
rowHeight | row-height | number | — | — |
placement | placement | 'bottom-start' | 'bottom-end' | 'top-start' | 'top-end' | 'bottom-start' | — |
matchTriggerWidth | match-trigger-width | boolean | true | — |
maxHeight | max-height | number | — | — |
name | name | string | '' | Yes |
| Method | Parameters |
|---|---|
focusInput() | none |
showMenu() | none |
closeMenu() | none |
loadOptions() | source: SelectOptionInit[] | OptionRowSource |
getLabels() | values: string[] |
getValidity() | none |
checkValidity() | none |
reportValidity() | none |
setCustomValidity() | message: string |
| Slot | Description |
|---|---|
loader | Replaces the default trigger busy spinner |
dropdown-icon | Replaces the caret glyph |
Override on the host element for per-instance theming:
| Property | Description |
|---|---|
--md-autocomplete-width | Trigger width (default 100% of container) |
--md-autocomplete-min-width | Minimum trigger width |
--md-autocomplete-chip-gap | Gap between selected chips |
--md-autocomplete-chip-radius | Chip corner radius |
--md-autocomplete-caret-color | Trailing caret color |
--md-autocomplete-caret-size | Caret glyph size (default 24px) |
--md-autocomplete-clear-color | Clear button color |
--md-autocomplete-option-icon-size | Leading-icon size in items |
--md-autocomplete-loading-color | Color of the loading row |
--md-autocomplete-spinner-size | Trigger busy-spinner size (default 22px) |
--md-autocomplete-option-icon-color | — |
Style internal elements through shadow DOM with ::part():
| Part | Description |
|---|---|
chip | — |
chips | Selected chips container |
clear | Trailing clear button |
field | Inner md-text-field |
loading-spinner | Trigger busy spinner (replaces clear/caret) |
caret | Trailing dropdown caret |
option-icon | — |
menu | Popup md-menu |
loading | Loading row inside the menu |
loading-progress | — |
chip-remove | — |
chip-label | — |
label names the field. The suggestion count is announced through a live
region driven by status-template — keep the {count} placeholder when you
translate it.Enter commits, Escape closes.Remove {label}. Note that name and
the clear button’s "Clear value" are hard-coded English; neither has a prop
yet.reserve-supporting-space avoids layout jump when errors appear.RTL — field, chips, caret and menu mirror under dir="rtl".
chip-position="left" / "right" is physical, so re-check it per
direction. See RTL.
Density — density="-1…-4" compacts the field, the chips and the
suggestion rows. Chips bottom out at a 24px floor (a tap-target guard in
md-chip), so they stop shrinking around -2 while the
field and rows keep going. See Density.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-autocomplete multiple label="Tags"></md-autocomplete>
<md-autocomplete multiple density="-1" label="Tags"></md-autocomplete>
<md-autocomplete multiple density="-2" label="Tags"></md-autocomplete>
<md-autocomplete multiple density="-3" label="Tags"></md-autocomplete>
<md-autocomplete multiple density="-4" label="Tags"></md-autocomplete>
<script type="module">
const tags = [
{ value: 'design', label: 'Design' },
{ value: 'eng', label: 'Engineering' },
{ value: 'prd', label: 'Product' },
{ value: 'ops', label: 'Operations' },
];
// options also takes a JSON attribute; value has no attribute form, so it is
// assigned here. Every framework below binds both directly instead.
for (const el of document.querySelectorAll('md-autocomplete')) {
el.options = tags;
el.value = ['design', 'eng'];
}
</script>import { MdAutocomplete } from '@awc-ui/react';
const TAGS = [
{ value: 'design', label: 'Design' },
{ value: 'eng', label: 'Engineering' },
{ value: 'prd', label: 'Product' },
{ value: 'ops', label: 'Operations' },
];
const SELECTED = ['design', 'eng'];
export function DensityScale() {
// The wrapper forwards options / value as PROPERTIES, so the arrays go
// straight through as props — no ref, no effect.
return (
<>
{[0, -1, -2, -3, -4].map((density) => (
<MdAutocomplete
key={density}
multiple
density={density}
label="Tags"
options={TAGS}
value={SELECTED}
/>
))}
</>
);
}import { Component } from '@angular/core';
@Component({
selector: 'app-density-scale',
template: `
<md-autocomplete
*ngFor="let density of densities"
multiple
[density]="density"
label="Tags"
[options]="tags"
[value]="selected"
></md-autocomplete>
`,
})
export class DensityScaleComponent {
densities = [0, -1, -2, -3, -4];
selected = ['design', 'eng'];
tags = [
{ value: 'design', label: 'Design' },
{ value: 'eng', label: 'Engineering' },
{ value: 'prd', label: 'Product' },
{ value: 'ops', label: 'Operations' },
];
}<script setup lang="ts">
import '@awc-ui/core/define';
const densities = [0, -1, -2, -3, -4];
const selected = ['design', 'eng'];
const tags = [
{ value: 'design', label: 'Design' },
{ value: 'eng', label: 'Engineering' },
{ value: 'prd', label: 'Product' },
{ value: 'ops', label: 'Operations' },
];
</script>
<template>
<md-autocomplete
v-for="density in densities"
:key="density"
multiple
:density="density"
label="Tags"
:options="tags"
:value="selected"
/>
</template><script lang="ts">
import '@awc-ui/core/define';
const densities = [0, -1, -2, -3, -4];
const selected = ['design', 'eng'];
const tags = [
{ value: 'design', label: 'Design' },
{ value: 'eng', label: 'Engineering' },
{ value: 'prd', label: 'Product' },
{ value: 'ops', label: 'Operations' },
];
</script>
{#each densities as density}
<md-autocomplete
multiple
{density}
label="Tags"
options={tags}
value={selected}
></md-autocomplete>
{/each}i18n — translate label, placeholder, supporting-text, error-text,
no-options-text, no-results-text, loading-text, value-missing-label,
and status-template (keeping {count}). Every one of them defaults to
English.
| Custom property | Purpose | Default |
|---|---|---|
--md-autocomplete-width / --md-autocomplete-min-width | Field inline-size | auto |
--md-autocomplete-chip-gap / --md-autocomplete-chip-radius | Chip rail metrics | Per density |
--md-autocomplete-caret-color / --md-autocomplete-caret-size | Dropdown caret | on-surface-variant |
--md-autocomplete-clear-color | Clear affordance | on-surface-variant |
--md-autocomplete-option-icon-size / --md-autocomplete-option-icon-color | Suggestion row icons | 24px |
--md-autocomplete-loading-color / --md-autocomplete-spinner-size | Loading state | primary |
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-autocomplete
label="Themed"
variant="outlined"
style="--md-autocomplete-width: 300px; --md-autocomplete-caret-color: var(--md-sys-color-primary); --md-autocomplete-chip-radius: 4px;">
<md-select-option value="a">Alpha</md-select-option>
<md-select-option value="b">Bravo</md-select-option>
<md-select-option value="c">Charlie</md-select-option>
</md-autocomplete>import { MdAutocomplete, MdSelectOption } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdAutocomplete
label="Themed"
variant="outlined"
style={{ '--md-autocomplete-width': '300px', '--md-autocomplete-caret-color': 'var(--md-sys-color-primary)', '--md-autocomplete-chip-radius': '4px' }}>
<MdSelectOption value="a">Alpha</MdSelectOption>
<MdSelectOption value="b">Bravo</MdSelectOption>
<MdSelectOption value="c">Charlie</MdSelectOption>
</MdAutocomplete>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-autocomplete
label="Themed"
variant="outlined"
style="--md-autocomplete-width: 300px; --md-autocomplete-caret-color: var(--md-sys-color-primary); --md-autocomplete-chip-radius: 4px;">
<md-select-option value="a">Alpha</md-select-option>
<md-select-option value="b">Bravo</md-select-option>
<md-select-option value="c">Charlie</md-select-option>
</md-autocomplete><script setup>
import '@awc-ui/core/define';
</script>
<template>
<md-autocomplete
label="Themed"
variant="outlined"
style="--md-autocomplete-width: 300px; --md-autocomplete-caret-color: var(--md-sys-color-primary); --md-autocomplete-chip-radius: 4px;">
<md-select-option value="a">Alpha</md-select-option>
<md-select-option value="b">Bravo</md-select-option>
<md-select-option value="c">Charlie</md-select-option>
</md-autocomplete>
</template><script>
import '@awc-ui/core/define';
</script>
<md-autocomplete
label="Themed"
variant="outlined"
style="--md-autocomplete-width: 300px; --md-autocomplete-caret-color: var(--md-sys-color-primary); --md-autocomplete-chip-radius: 4px;">
<md-select-option value="a">Alpha</md-select-option>
<md-select-option value="b">Bravo</md-select-option>
<md-select-option value="c">Charlie</md-select-option>
</md-autocomplete>Every default resolves through an md-sys-color role, so an autocomplete that
sets no custom properties follows the theme on its own:
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-autocomplete label="Untouched defaults" style="min-inline-size: 280px;">
<md-select-option value="lon">London</md-select-option>
<md-select-option value="par">Paris</md-select-option>
<md-select-option value="ber">Berlin</md-select-option>
</md-autocomplete>import { MdAutocomplete, MdSelectOption } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdAutocomplete label="Untouched defaults" style={{ minInlineSize: '280px' }}>
<MdSelectOption value="lon">London</MdSelectOption>
<MdSelectOption value="par">Paris</MdSelectOption>
<MdSelectOption value="ber">Berlin</MdSelectOption>
</MdAutocomplete>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-autocomplete label="Untouched defaults" style="min-inline-size: 280px;">
<md-select-option value="lon">London</md-select-option>
<md-select-option value="par">Paris</md-select-option>
<md-select-option value="ber">Berlin</md-select-option>
</md-autocomplete><script setup>
import '@awc-ui/core/define';
</script>
<template>
<md-autocomplete label="Untouched defaults" style="min-inline-size: 280px;">
<md-select-option value="lon">London</md-select-option>
<md-select-option value="par">Paris</md-select-option>
<md-select-option value="ber">Berlin</md-select-option>
</md-autocomplete>
</template><script>
import '@awc-ui/core/define';
</script>
<md-autocomplete label="Untouched defaults" style="min-inline-size: 280px;">
<md-select-option value="lon">London</md-select-option>
<md-select-option value="par">Paris</md-select-option>
<md-select-option value="ber">Berlin</md-select-option>
</md-autocomplete>| Part | Element |
|---|---|
field | The text-field trigger |
chips / chip | Selected-value chips, in multiple mode |
chip-remove / chip-label | Forwarded out of each chip (exportparts) |
clear / caret | Trailing controls |
menu | The suggestion popup |
option / option-selected | Suggestion rows |
option-icon | Per-option icon |
loading-spinner | Trailing spinner while busy |
loading / loading-progress | The in-menu progress affordance |
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<style>
.parts-ac::part(caret) { color: var(--md-sys-color-primary); }
.parts-ac::part(chip) { border-radius: 4px; }
</style>
<md-autocomplete class="parts-ac" multiple label="Styled parts" style="min-inline-size: 280px;">
<md-select-option value="lon">London</md-select-option>
<md-select-option value="par">Paris</md-select-option>
</md-autocomplete>import { MdAutocomplete, MdSelectOption } from '@awc-ui/react';
export function Demo() {
return (
<>
<style>
.parts-ac::part(caret) { color: var(--md-sys-color-primary); }
.parts-ac::part(chip) { border-radius: 4px; }
</style>
<MdAutocomplete className="parts-ac" multiple label="Styled parts" style={{ minInlineSize: '280px' }}>
<MdSelectOption value="lon">London</MdSelectOption>
<MdSelectOption value="par">Paris</MdSelectOption>
</MdAutocomplete>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<style>
.parts-ac::part(caret) { color: var(--md-sys-color-primary); }
.parts-ac::part(chip) { border-radius: 4px; }
</style>
<md-autocomplete class="parts-ac" multiple label="Styled parts" style="min-inline-size: 280px;">
<md-select-option value="lon">London</md-select-option>
<md-select-option value="par">Paris</md-select-option>
</md-autocomplete><script setup>
import '@awc-ui/core/define';
</script>
<template>
<style>
.parts-ac::part(caret) { color: var(--md-sys-color-primary); }
.parts-ac::part(chip) { border-radius: 4px; }
</style>
<md-autocomplete class="parts-ac" multiple label="Styled parts" style="min-inline-size: 280px;">
<md-select-option value="lon">London</md-select-option>
<md-select-option value="par">Paris</md-select-option>
</md-autocomplete>
</template><script>
import '@awc-ui/core/define';
</script>
<style>
.parts-ac::part(caret) { color: var(--md-sys-color-primary); }
.parts-ac::part(chip) { border-radius: 4px; }
</style>
<md-autocomplete class="parts-ac" multiple label="Styled parts" style="min-inline-size: 280px;">
<md-select-option value="lon">London</md-select-option>
<md-select-option value="par">Paris</md-select-option>
</md-autocomplete>md-select ·
md-multi-select ·
md-search ·
md-text-field ·
md-chip ·
md-menu ·
md-transfer-list
md-autocompleteTwo artefacts to give your AI agent so it generates correct UI with this component. The per-component spec answers "how do I use this exact tag?". The main-llm spec answers "which tag should I pick in the first place?".
md-autocomplete spec card
Identity · when to use / when NOT · decision cues · behavioural contract · do/don't · anti-patterns · full API. Paste into your agent when you're implementing with this component.
System-prompt preamble · decision matrix · token reference · page recipes · anti-patterns. Paste into the system prompt at the start of a piece of work.