Combobox
Searchable native input trigger with projected options, local filtering, and async mode.
Combobox composes field, dropdown, option, and the highlight pipe with the combobox directive.
import {
KuiCombobox,
KuiComboboxHighlightPipe,
KuiDropdown,
KuiField,
KuiOption,
provideKuiDefaults,
} from '@kikita-labs/ui';Bind value and query with Angular signals. Filter and render projected options yourself.
<kui-field label="Assignee" hint="Type to filter projected options">
<input
kuiCombobox
[(value)]="assignee"
[(query)]="query"
[kuiLabelFn]="personLabel"
placeholder="Search people..."
(search)="query.set($event)"
/>
<kui-dropdown>
@for (person of filteredPeople(); track person.id) {
<button kuiOption [value]="person">
<span class="kui-combobox-match-label">
@for (segment of person.name | kuiComboboxHighlight: query(); track $index) {
@if (segment.match) {
<mark class="kui-combobox-highlight">{{ segment.text }}</mark>
} @else {
<span>{{ segment.text }}</span>
}
}
</span>
</button>
} @empty {
<div class="kui-combobox-empty">No people found</div>
}
</kui-dropdown>
</kui-field>Use (search) for remote requests. The directive does not own async data; update the projected options when results arrive. Use mode="async" to document that filtering happens outside the directive.
<kui-field label="Reviewer" hint="Simulates a remote lookup with a loading row">
<input
kuiCombobox
mode="async"
[(value)]="reviewer"
[(query)]="reviewerQuery"
[loading]="loading()"
[kuiLabelFn]="personLabel"
placeholder="Type to search..."
(search)="loadReviewers($event)"
/>
<kui-dropdown>
@if (loading()) {
<div class="kui-combobox-loading-row">
<span class="kui-combobox-loader" aria-hidden="true"></span>
Loading people
</div>
} @else if (errorMessage(); as lookupError) {
<div class="kui-combobox-empty" role="alert">{{ lookupError }}</div>
} @else {
@for (person of reviewers(); track person.id) {
<button kuiOption [value]="person">{{ person.name }}</button>
} @empty {
<div class="kui-combobox-empty">No matches</div>
}
}
</kui-dropdown>
</kui-field>mode="free" stores typed text as the value. In the default filter mode, editing text clears the selected value until a projected option is chosen.
<kui-field label="Tag" hint="Type a custom tag or choose a suggestion">
<input kuiCombobox mode="free" [(value)]="tag" placeholder="Type or choose..." />
<kui-dropdown>
<button kuiOption value="Bug">Bug</button>
<button kuiOption value="Feature">Feature</button>
<button kuiOption value="Regression">Regression</button>
</kui-dropdown>
</kui-field>Combobox inherits field id, label association, aria-describedby, invalid state, and field size from kui-field.
<div class="combobox-field-states-example">
<kui-field label="Owner" hint="Disabled prevents editing and opening the dropdown">
<input kuiCombobox disabled [(value)]="disabledOwner" placeholder="Search people..." />
<kui-dropdown>
<button kuiOption value="engineer">Software Engineer</button>
<button kuiOption value="designer">Designer</button>
</kui-dropdown>
</kui-field>
<kui-field label="Owner" error="Choose an owner before continuing">
<input kuiCombobox invalid [(value)]="invalidOwner" placeholder="Search people..." />
<kui-dropdown>
<button kuiOption value="engineer">Software Engineer</button>
<button kuiOption value="designer">Designer</button>
</kui-dropdown>
</kui-field>
</div>Set defaults.combobox once for the application or for a subtree. Each option resolves as local input > defaults.combobox.<option> > built-in default.
import { provideKikitaUi } from '@kikita-labs/ui';
export const appConfig: ApplicationConfig = {
providers: [
provideKikitaUi({
defaults: {
combobox: {
/* options below */
},
},
}),
],
};clearable resolves as local input > defaults.combobox > defaults.field > true. The chevron and clear icons take precedence over defaults.icons.pickerChevron and defaults.icons.clear.
| Name | Type | Default | Description |
|---|---|---|---|
| clearable | boolean | — | When true, field controls with clear affordances show a clear button by default. |
| chevronIcon | KuiIconGlyph | — | Icon of the options toggle. Takes precedence over defaults.icons.pickerChevron. See Structural Icons. |
| clearIcon | KuiIconGlyph | — | Icon of the clear button. Takes precedence over defaults.icons.clear. See Structural Icons. |
Set any of these on the component or an ancestor to restyle one part. Each token is optional: when it is not set, the part uses the role or scale token in the default column.
| Name | Type | Default | Description |
|---|---|---|---|
| --kui-combobox-chevron-color-expanded | CSS custom property | --kui-color-primary-text | Chevron color, expanded |
| --kui-combobox-loader-color | CSS custom property | --kui-color-primary-indicator | Loader color |
| --kui-combobox-empty-color | CSS custom property | --kui-color-text-secondary | Empty color |
| --kui-combobox-loader-track-color | CSS custom property | --kui-color-border | Loader track color |
| Name | Type | Default | Description |
|---|---|---|---|
| --kui-combobox-loader-radius | CSS custom property | --kui-radius-full | Loader corner radius |
| --kui-combobox-match-label-font-size | CSS custom property | --kui-text-sm-size | Match label font size |
| --kui-combobox-empty-padding-block | CSS custom property | --kui-space-4 | Empty padding, block |
| --kui-combobox-empty-padding-inline | CSS custom property | --kui-space-3 | Empty padding, inline |
| --kui-combobox-empty-font-size | CSS custom property | --kui-text-sm-size | Empty font size |
| --kui-combobox-loading-row-gap | CSS custom property | --kui-space-2 | Loading row gap |
| --kui-combobox-padding-inline-end | CSS custom property | 34px | Input end padding that clears the chevron |
| --kui-combobox-padding-inline-end-clearable | CSS custom property | 56px | Input end padding that clears the clear button and the chevron |
Every text the component owns (accessible names, visible words, announcements) is a typed message with an English default. Override it for the application with provideKikitaUi({ messages }), or for a subtree with provideKuiMessages.
| Name | Type | Default | Description |
|---|---|---|---|
| openOptions | string | — | Name of the open button. Default Open options. |
| closeOptions | string | — | Name of the close button. Default Close options. |
Inputs and outputs verified against @kikita-labs/ui v2.0.0 public typings.
| Name | Type | Default | Description |
|---|---|---|---|
| value | T | string | null | null | Selected value. Bound by [formField] or [(value)]. |
| query | string | '' | Current search text shown in the native input. |
| search | output: string | - | Emitted on every native input edit. Use for local filtering or remote requests. |
| kuiLabelFn | (item: T) => string | String() | Maps a selected object value to display text. Required when T is not a primitive. |
| placeholder | string | '' | Native input placeholder shown when no value is selected. |
| mode | 'filter' | 'free' | 'async' | 'filter' | filter clears the value while editing until a kuiOption is selected. free stores typed text as the value. async documents that filtering happens outside the directive. |
| clearable | boolean | undefined | true | Shows a clear affordance. Falls back to defaults.combobox, then defaults.field, then true. |
| loading | boolean | false | Shows a suffix loader. Loading row content inside kui-dropdown is projected by the consumer. |
| disabled | boolean | false | Disables the native input. Set by [formField] or directly. |
| readonly | boolean | false | Keeps the value readable but prevents editing and opening the dropdown. |
| invalid | boolean | false | Applies ARIA invalid state. kui-field also contributes invalid state from validation. |
| errors | readonly ValidationError[] | [] | Validation errors set by [formField], consumed by kui-field for automatic error text. |
| touched | boolean | false | Touched state set by [formField] or Signal Forms. |
| touch | output: void | - | Emitted when an opened dropdown closes; marks the control touched for Signal Forms. |
| id | string | undefined | undefined | Explicit id override. Inside kui-field, the field id is used when omitted. |
| kuiOption | directive | - | Marks a projected option inside kui-dropdown. Provides role="option", aria-selected, disabled state, and keyboard navigation. |
| kuiComboboxHighlight | pipe: (label: string, query: string | null | undefined) => readonly { text: string; match: boolean }[] | - | Splits an option label into plain and matched segments for highlighting the current query. |
| provideKuiDefaults({ combobox }) | (defaults: KuiComponentDefaults) => Provider | - | Registers combobox defaults, such as clearable, for a subtree. Use provideKikitaUi({ defaults }) for the whole application. |
| KuiComboboxOptions | interface | - | Shape of the defaults.combobox key, read by the directive for the clearable fallback. |
| --kui-combobox-affordance-size | CSS custom property | - | Size of the suffix clear/chevron affordance controls. |
| --kui-combobox-suffix-gap | CSS custom property | - | Gap between suffix affordances (clear button, chevron, loader). |
| --kui-combobox-loader-size | CSS custom property | - | Diameter of the suffix loading spinner. |
| --kui-combobox-loader-border-width | CSS custom property | - | Border width of the suffix loading spinner. |
| --kui-combobox-loader-duration | CSS custom property | - | Animation duration of the suffix loading spinner. |
| --kui-combobox-highlight-bg | CSS custom property | - | Background color of matched text inside kui-combobox-highlight. |
| --kui-combobox-highlight-text | CSS custom property | - | Text color of matched text inside kui-combobox-highlight. |
| --kui-combobox-highlight-radius | CSS custom property | - | Corner radius of the matched-text highlight mark. |
Combobox keeps native input semantics and composite listbox behavior on the dropdown.
- The host remains a native
<input>. - The input uses
role="combobox",aria-haspopup="listbox",aria-expanded, andaria-controls. kui-fieldprovides label, hint, error, required marker,aria-describedby, and inherited invalid state.kui-dropdownrenders the popup listbox through Angular CDK overlay.kuiOptionprovidesrole="option",aria-selected, disabled state, click selection, and keyboard navigation.- Arrow keys open the dropdown and move focus to the first or last enabled option.
- Escape closes the dropdown and returns focus to the control.
- Choosing an option inside the panel returns focus to the input. With Angular Signal Forms the field gates the required error and
aria-invaliduntil the control is touched, and selecting a valid option clears that state. - Multiple chip-backed selection is intentionally not part of Combobox; use Select for fixed choices without typing.
Consumer-visible changes of @kikita-labs/ui 2.0 for Combobox.
- The classes are
KuiCombobox,KuiDropdown,KuiFieldandKuiOption(they had aDirectiveorComponentsuffix);ng update @kikita-labs/uirenames the imports. KUI_COMBOBOX_OPTIONSis removed andkuiProvideComboboxOptionsis deprecated until 3.0: setdefaults.comboboxthroughprovideKuiDefaultsorprovideKikitaUi({ defaults }).- The control no longer pulls the whole field and dropdown into your bundle, the panel returns focus to the control after a selection, and the open and close button names come from the
comboboxmessages.