CngxCombobox
projects/forms/select/combobox/combobox.component.ts
Import#
import { CngxCombobox } from '@cngx/forms/select'
Description#
Tag-input style multi-value combobox. Inline <input role="combobox">
next to a chip strip; typing filters the panel live, Backspace-on-empty
removes the trailing chip.
Stateless graph in createSelectCore; this component is a thin adapter binding the trigger template to the core's signals plus value-shape write-paths (AD dispatch, Field↔sync, search filter).
Metadata#
Host#
Providers#
CNGX_FORM_FIELD_CONTROL- useExisting
CngxCombobox CNGX_STATEFUL- useFactory
(): { readonly state: CngxAsyncState} => { const self = inject(CngxCombobox); return { state: self.commitState }; } CNGX_SELECT_PANEL_HOST- useExisting
CngxCombobox CNGX_SELECT_PANEL_VIEW_HOST- useExisting
CngxCombobox
Relationships
Depends on13
Index#
Inputs
Outputs
Derived State
HostBindings
Inputs#
CngxSelectAnnouncerConfig['format'] | nullPer-instance formatter override.
nullstring | nullExplicit aria-label on the trigger.
null, { alias: 'aria-label' }string | nullExplicit aria-labelledby on the trigger.
null, { alias: 'aria-labelledby' }TemplateRef | null Replaces the built-in ▾ caret glyph. Ignored when
*cngxSelectCaret is projected.
nullNonNullableChip-strip overflow strategy. See CngxSelectConfig.chipOverflow.
this.config.chipOverflow, A11y label prefix for the per-chip remove button.
this.config.ariaLabels?.chipRemove ?? 'Remove'Render a clear-all button when at least one value is selected.
falseA11y label for the clear-all button.
this.config.ariaLabels?.clearButton ?? 'Reset selection', TemplateRef | null Replaces the built-in ✕ glyph inside the default clear-all button.
Ignored when *cngxSelectClearButton is projected.
nullWhether activating an option closes the panel. Default false
(tag-input UX).
falseCngxSelectCommitErrorDisplayWhere commitAction errors render without a *cngxSelectCommitError template.
this.config.commitErrorDisplayCngxSelectCompareFnEquality function used to match values to options. Defaults to Object.is.
cngxSelectDefaultCompare as CngxSelectCompareFn<T>, Disabled state. Merges with presenter.disabled().
false, { alias: 'disabled' }NonNullableMobile enterkeyhint. Default 'enter' - Combobox commits Enter
without closing the panel.
this.config.enterKeyHint ?? 'enter', Hide the default in-panel checkmark.
!this.config.showSelectionIndicatorstring | nullCustom id. Defaults to the presenter-generated ID inside form-field.
null, { alias: 'id' }NonNullableMobile inputmode attribute. Defaults from CngxSelectConfig.inputMode.
this.config.inputModeCngxSelectLoadingVariantFirst-load indicator variant.
this.config.loadingVariantCngxSelectOptionsInputOptions in data-driven mode (flat or grouped).
[] as CngxSelectOptionsInput<T>string | readonly string[] | nullClasses applied to the panel root. Merged with the library default.
nullPopoverPlacementPopover placement relative to the trigger. Per-instance input wins
over CngxSelectConfig.popoverPlacement.
this.config.popoverPlacementCngxSelectRefreshingVariantSubsequent-load ("refreshing") indicator variant.
this.config.refreshingVariantRequired state. Merges with presenter.required().
false, { alias: 'required' }Debounce for search term updates (ms).
this.config.typeaheadDebounceIntervalListboxMatchFn | nullCustom matcher for the inline CngxListboxSearch.
null'before' | 'after' | nullIndicator position. null → inherit config.
nullCngxSelectSelectionIndicatorVariant | nullIndicator variant. null → inherit config.
nullSkeleton-row count when loadingVariant === 'skeleton'.
this.config.skeletonRowCountSuppress the first searchTermChange emission (hydrate-time '').
falseOutputs#
unknownCngxComboboxChangeAsyncStatusInstance Properties#
unknownCallback invoked when the user clicks the default retry button.
input<(() => void) | null>(null)unknowncomputed<CngxSelectOptionDef<T>[]>(
() => {
const vals = this.values();
if (vals.length === 0) {
return [];
}
// Resolve selected values against the UNFILTERED option merge so
// chips for previously-picked values remain visible when the
// search term hides the matching option from the listbox.
const eq = this.compareWith();
const out: CngxSelectOptionDef<T>[] = [];
const flat = this.core.unfilteredFlatOptions();
for (const v of vals) {
const match = flat.find((o) => eq(o.value, v));
if (match) {
out.push(match);
}
}
return out;
},
{
equal: (a, b) => {
if (a === b) {
return true;
}
if (a.length !== b.length) {
return false;
}
for (let i = 0; i < a.length; i++) {
if (!Object.is(a[i], b[i])) {
return false;
}
}
return true;
},
},
)Methods#
patchData(item: CngxSelectOptionDef<T>)Append a pre-built option to the local buffer. Renders in the next
panel emission and silently drops once the server includes a
matching value. Idempotent under compareWith.
HostBindings#
| Binding | Expression |
|---|---|
[id] | resolvedId() |
[attr.aria-readonly] | ariaReadonly() |
Structural styles shared across the entire CngxSelect family -
imported via styleUrls from every variant (CngxSelect,
CngxMultiSelect, CngxCombobox, CngxTypeahead, CngxTreeSelect,
CngxReorderableMultiSelect, CngxActionSelect,
CngxActionMultiSelect, CngxSelectShell, and the panel components).
All variants run encapsulation: None so the rules below are global;
the .cngx-select__* BEM-style class prefix is the namespace.
State modifiers
.cngx-option--highlightedon an option row - primary-tinted background marking the keyboard-focused row[aria-disabled='true']on an option - opacity 0.5 +cursor: not-allowed.cngx-select__option--pending-cursor: progresswhile a row's commit is in flight.cngx-select__chip-list--reordering- whole-stripcursor: grabbingwhile a drag is active.cngx-select__chip--draggingon a chip - Trello-style lift (shadow + scale + tilt + accent fill).cngx-select__chip--drag-overon a chip - vertical drop bar painted on the leading edge via::before
Variants
Chip-list overflow modes via data-overflow:
- default - flex-wrap chips across multiple rows
data-overflow='scroll-x'- single row + horizontal scrolldata-overflow='truncate'- single row + clipped overflow + the.cngx-select__chip-overflow-badgecount pill
Error surfaces with two layouts:
.cngx-select__error- column-stacked block (message + retry).cngx-select__error--inline- inline banner above the option list.cngx-select__commit-error- per-commit failure banner
Slots
.cngx-select__panel- the floating panel surface (anchor-positioned viaposition-try-fallbacks).cngx-select__group-header- uppercase optgroup label.cngx-select__option/.cngx-select__check- option row + the selected-state checkmark.cngx-select__loading/.cngx-select__empty- inline message rows.cngx-select__skeleton/.cngx-select__skeleton-row- first-load shimmer placeholders (static underprefers-reduced-motion).cngx-select__spinner-wrap/.cngx-select__spinner- first-load spinner ring.cngx-select__loading-bar- first-load top-edge progress bar.cngx-select__refreshing/.cngx-select__refreshing-spinner/.cngx-select__refreshing-dots- subsequent-load indicators.cngx-select__option-spinner/.cngx-select__option-error- per-row commit feedback.cngx-select__chip-list/.cngx-select__chip-overflow-badge- chip strip + overflow count.cngx-select__chip-wrap/.cngx-select__chip-handle- reorderable chip surface + optional drag-handle glyph.cngx-select__error/.cngx-select__error--inline/.cngx-select__error-message/.cngx-select__error-retry/.cngx-select__commit-error- error block, banner, and retry pieces
Inheritance
Tokens are registered top-level in @layer cngx.tokens via @property
so any consumer (cngx.theme layer, app layer) can override without
learning the type.
Panel surface, every primary-tinted glyph, and every danger-toned error surface delegate to the foundation so a dark-mode swap cascades through the floating panel even when anchor positioning detaches it from the trigger's DOM context:
--cngx-select-panel-border->--cngx-color-border--cngx-select-panel-bg->--cngx-color-surface--cngx-select-panel-color->--cngx-color-text--cngx-select-panel-shadow->--cngx-shadow-md--cngx-select-check-color->--cngx-color-primary--cngx-select-spinner-color->--cngx-color-primary--cngx-select-loading-bar-color->--cngx-color-primary--cngx-select-refreshing-color->--cngx-color-primary--cngx-select-option-spinner-color->--cngx-color-primary--cngx-select-option-error-color->--cngx-color-danger--cngx-select-error-color->--cngx-color-danger--cngx-select-chip-remove-hover-color->--cngx-color-danger--cngx-select-chip-drag-bg->--cngx-color-primary--cngx-select-chip-drop-bar-color->--cngx-color-primary--cngx-select-trigger-invalid-border-color->--cngx-color-danger--cngx-select-trigger-invalid-outline-color->--cngx-color-danger
--cngx-select-panel-min-width is intentionally NOT registered via
@property so the anchor-size(width) fallback survives when the
consumer doesn't override it; registering it with any initial-value
would shadow the var() fallback and break anchor positioning.
Dark mode
Three hooks swap alpha-on-black surfaces (panel shadow, option highlight, spinner track, chip-overflow tint, chip remove-hover tint, chip handle muted, chip drag shadow) plus the skeleton shimmer gradient and the option-spinner track:
prefers-color-scheme: dark[data-color-scheme="dark"].darkclass
Foundation-delegated tokens ride the foundation cascade unchanged.
Pair with
@cngx/themes/material/select-theme- Material 3 surface treatment across the entire family
Trigger skin for CngxCombobox - tag-input variant where the inline
<input> IS the combobox's focusable element. The host carries
.cngx-combobox; structural parts shared with the rest of the select
family (panel frame, option rows, loading variants, chip-list
flex-wrap layout, error surfaces) ride on shared/select-base.css.
This file owns the role="group" wrapper, the inline <input>, the
caret glyph, and the clear-all button.
State modifiers
:focus-withinon.cngx-combobox__trigger- focus-ring outline- offset
[aria-disabled='true']on the trigger - opacity dim +cursor: not-allowed:has(> [role='combobox'][aria-invalid='true'])on the trigger - danger border + box-shadow glow at focus time (paints on the visible wrapper without a TS-side rebind sincerole="combobox"lives on the input).cngx-combobox__input::placeholder- muted placeholder via the shared--cngx-select-placeholder-colortoken:hoveron.cngx-combobox__clear-all- opacity 1:focus-visibleon the clear-all button - 2px primary outline
Slots
.cngx-combobox__root-display: contentshost shim.cngx-combobox__trigger- the bordered row.cngx-combobox__input- therole="combobox"text input.cngx-combobox__trigger-label-*cngxComboboxTriggerLabelslot for text summary.cngx-combobox__clear-all- reset-all button.cngx-combobox__caret- panel-state glyph
Inheritance
Trigger tokens delegate up the family chain:
--cngx-combobox-*->--cngx-select-*-> hardcoded--cngx-combobox-border->--cngx-color-border--cngx-combobox-focus-outline->--cngx-color-primary- invalid surfaces fall back to
--cngx-select-trigger-invalid-*->--cngx-color-danger
Once registered via @property the inner var() steps are
documentation-only; system tokens (--cngx-color-*) are the
brand-wide override path.
Pair with
@cngx/themes/material/select-theme- Material 3 surface treatment shared across the entire select family
Index#
Surface
Layout
State / Highlighted
State / Selected
State / Disabled
State / Loading
State / Refreshing
State / Commit
State / Remove
State / Dragging
State / Error
State / Trigger invalid
State / Focus
State / Clear
Surface
*1px solid oklch(0.85 0.01 250)Border shorthand of the dropdown panel. Falls back through
--cngx-color-border. inherits: true so the :root delegating
value reaches the floating panel host (anchor-positioned, may
sit outside the normal DOM flow).
See: [[--cngx-color-border]]
<color>oklch(1 0 0)Background of the dropdown panel. Falls back through
--cngx-color-surface.
See: [[--cngx-color-surface]]
*currentColorText color inside the panel. syntax: '*' + initial-value
currentColor lets the panel inherit text color from its
ancestor by default. Registering as <color> would forbid
currentColor since <color> requires a computationally-
independent value.
*0 4px 12px oklch(0 0 0 / 0.12)Drop-shadow shorthand. Falls back through --cngx-shadow-md.
inherits: true so the :root dark-mode override (deeper shadow
on dark) reaches the floating panel host.
<color>oklch(0 0 0 / 0.5)Color of the placeholder text shown when no value is selected.
<color>oklch(0 0 0 / 0.5)Color of the dropdown caret glyph shared across every variant.
Muted by default to mirror the placeholder token; the Material
bridge maps it to --mat-sys-on-surface-variant. inherits: true
so a trigger-level override reaches the glyph element.
<color>oklch(0 0 0 / 0.5)Color of the clear-button glyph shared across every variant. Tracks
the same muted default as the caret so both trigger affordances read
uniformly. inherits: true so a trigger-level override reaches the
glyph element.
*1px solid oklch(0.85 0.01 250)Border shorthand of the trigger.
Layout
*16remMaximum height before vertical scrolling kicks in.
*autoMinimum height of an option row - defaults to auto so dense
rows stay compact.
*1.25emFont-size of the dropdown caret glyph shared across every variant.
*0.25remCorner radius of the reorderable chip wrap container.
*0.25remGap between the chip body and any projected drag handle.
*0.375remGap between the chip list, input, caret, and clear-all button.
*2.25remMinimum block size of the trigger - fits a single row of chips.
*4remFlex basis of the inline input - sets the preferred starting width before the input grows or shrinks.
*4remMinimum inline size of the inline input - keeps the input tappable even when chips dominate the trigger row.
State / Highlighted
<color>oklch(0.66 0.19 50 / 0.1)Background of the keyboard-highlighted option row. Defaults to a
low-alpha tint of --cngx-color-primary so the highlight reads
as brand-accented without competing with selection state.
See: [[--cngx-color-primary]]
State / Selected
<color>oklch(0.66 0.19 50)Color of the selected-option checkmark glyph. Falls back through
--cngx-color-primary.
See: [[--cngx-color-primary]]
State / Disabled
<number>0.5Opacity multiplier applied when the trigger is disabled. Shared family-wide so every variant dims its disabled trigger identically.
<number>0.5Opacity multiplier applied when the trigger is disabled.
State / Loading
*0.125remCorner radius of each skeleton placeholder row.
*2px solid oklch(0 0 0 / 0.15)Track stroke of the first-load spinner ring. inherits: true
so the :root dark-mode override (alpha-on-white on dark)
reaches the spinner inside the floating panel.
<color>oklch(0.66 0.19 50)Indicator stroke of the first-load spinner ring. Falls back to
--cngx-color-primary.
<color>oklch(0.66 0.19 50)Color of the first-load loading bar. Falls back to
--cngx-color-primary.
State / Refreshing
<length>2pxHeight of the subsequent-load refreshing bar.
<color>oklch(0.66 0.19 50)Color of the refreshing bar gradient. Falls back to
--cngx-color-primary.
*0.25remPadding around the refreshing spinner wrapper.
*0.375remPadding around the refreshing dots block.
State / Commit
<color>oklch(0.66 0.19 50)Indicator stroke of the per-row commit spinner.
<color>oklch(0.6 0.18 25)Glyph color of the per-row commit error indicator. Falls back to
--cngx-color-danger.
*0.375rem 0.5remPadding of the commit error banner.
State / Overflow
<color>oklch(0 0 0 / 0.08)Background of the chip overflow badge shown in truncate overflow mode.
<color>oklch(0 0 0 / 0.6)Text color of the chip overflow badge.
State / Remove
*1.25remHit-target diameter of the chip remove button inside a reorderable chip wrap.
<color>oklch(0 0 0 / 0.12)Background tint of the chip remove button on hover.
<color>oklch(0.6 0.18 25)Foreground color of the chip remove button on hover. Falls back
to --cngx-color-danger.
State / Reorder
<color>oklch(0.5 0.01 250)Color of the optional projected drag-handle glyph.
*0.75remFont-size of the optional projected drag-handle glyph.
State / Dragging
*0 8px 20px oklch(0 0 0 / 0.28)Drop-shadow of the chip lifted into the dragging state.
inherits: true so the :root dark-mode override (deeper shadow
on dark) reaches the chip while it's being dragged.
<color>oklch(0.66 0.19 50)Background of the chip lifted into the dragging state. Falls back
to --cngx-color-primary.
See: [[--cngx-color-primary]]
<color>oklch(1 0 0)Text color of the chip lifted into the dragging state.
<number>1.06Scale multiplier of the dragging chip - Trello-style lift.
<angle>-1.5degRotation tilt applied to the dragging chip - Trello-style lift.
<length>3pxWidth of the drop-indicator bar between chips.
<color>oklch(0.66 0.19 50)Color of the drop-indicator bar between chips. Falls back to
--cngx-color-primary.
State / Error
<color>oklch(0.6 0.18 25)Text color of every error surface. Falls back to
--cngx-color-danger.
See: [[--cngx-color-danger]]
*0.375rem 0.5remPadding of the inline error banner shown above the option list.
*1px solid currentColorBorder shorthand of the error retry button.
State / Trigger invalid
<color>oklch(0.6 0.18 25)Border color painted on the trigger wrapper when aria-invalid="true".
Defaults track --cngx-color-danger via the delegating :root entry below.
<length>1pxBorder width painted on the trigger wrapper when aria-invalid="true".
<color>oklch(0.6 0.18 25)Outline color layered on the invalid trigger when focus is inside it.
Defaults track --cngx-color-danger via the delegating :root entry below.
*0 0 0 3px oklch(0.6 0.18 25 / 0.2)Soft halo layered behind the invalid trigger at focus time. Authored as a
box-shadow so it composes with any consumer-supplied focus outline.
State / Focus
*2px solid oklch(0.66 0.19 50)Focus-ring outline shorthand applied on :focus-within. Falls
back to --cngx-color-primary.
See: [[--cngx-color-primary]]
State / Clear
Material theme
import { ChangeDetectionStrategy, Component, ViewEncapsulation, signal } from '@angular/core';
import { CngxCombobox, type CngxSelectOptionDef } from '@cngx/forms/select';
/**
* `CngxCombobox` rendered against a Material 3 palette via the published
* `@cngx/themes/material/select-theme` bridge.
*
* The stylesheet builds a real M3 theme: `mat.theme` emits the `--mat-sys-*`
* system tokens, then `select.theme($theme)` routes every `--cngx-select-*`
* token onto its Material counterpart, so the tag-input trigger, chip strip,
* and panel surface inherit the palette with no per-instance overrides.
* `ViewEncapsulation.None` lets the global `html` theme and the
* `:where(cngx-combobox)` bridge rules reach both the trigger and the
* top-layer panel.
*/
@Component({
selector: 'app-root',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
imports: [CngxCombobox],
styleUrl: './material-theme.component.scss',
template: `
<div class="demo">
<cngx-combobox
[label]="'Favorite colors'"
[options]="colors"
[(values)]="values"
placeholder="Type to filter…"
/>
<p class="demo__readout">Selected: {{ values().length ? values().join(', ') : '—' }}</p>
</div>
`,
})
export class MaterialThemeExample {
protected readonly values = signal<string[]>([]);
protected readonly colors: CngxSelectOptionDef<string>[] = [
{ value: 'red', label: 'Red' },
{ value: 'green', label: 'Green' },
{ value: 'blue', label: 'Blue' },
{ value: 'amber', label: 'Amber' },
{ value: 'violet', label: 'Violet' },
];
}
export { MaterialThemeExample as AppComponent };