Skip to main content
cngx-src documentation

CngxChipGroup

cngx-chip-group

ComponentPrimaryOnPushNo encapsulationv0.1.0WCAG AA

projects/common/interactive/chip-group/chip-group.component.ts

Import#

import { CngxChipGroup } from '@cngx/common/interactive'

Description#

Single-select chip group. Owns a selected = model<T | undefined> (the canonical single-value source) and exposes the parent contract via CNGX_CHIP_GROUP_HOST. Behaves as a role="listbox" with CngxRovingTabindex-driven keyboard navigation; consumers project <cngx-chip cngxChipInGroup> children that derive their own aria-selected from this group's isSelected(value).

Mode is static per feedback_select_family_split: this is the single-select half of a deliberate split. Consumers pick <cngx-chip-group> for single-pick semantics or <cngx-multi-chip-group> for multi-pick semantics - never a runtime [selectionMode] flag.

value is a structural alias of selected so the group satisfies CngxControlValue<T | undefined> without owning two synchronised models - both names point to the same ModelSignal<T | undefined> instance. Mirrors CngxCheckboxGroup's selectedValues / value pairing.

[state] accepts CngxAsyncState<unknown> for async-loaded chip lists; reactively drives aria-busy so AT announces the busy moment without consumer wiring. Slot directives for skeleton/empty/error are deferred - the harmonized Phase 3-4 group surface ships only the aria-busy projection of [state], and chip-group follows that precedent for cross-family consistency (plan deviation 2026-05-01: skeleton/empty/error slot directives declared in the plan body are dropped at execute time pending a cross-family harmonization pass).

Per Pillar 1 (Ableitung statt Verwaltung), aria-busy is a computed() from state.status() - never a manual write. Per Pillar 3 (Komposition statt Konfiguration), the group composes CngxRovingTabindex and emits no implicit children - consumers project chip rows themselves.

<cngx-chip-group label="Size" [(selected)]="size">
  <cngx-chip cngxChipInGroup [value]="'sm'">Small</cngx-chip>
  <cngx-chip cngxChipInGroup [value]="'md'">Medium</cngx-chip>
  <cngx-chip cngxChipInGroup [value]="'lg'">Large</cngx-chip>
</cngx-chip-group>

https://cngxjs.github.io/cngx/examples/#/common/interactive/chip/group/basic-pick-exactly-one-size https://cngxjs.github.io/cngx/examples/#/forms/field/form-primitives/coming-in-a-follow-up https://cngxjs.github.io/cngx/examples/#/forms/field/form-primitives/reactive-forms-same-atom-just-bind-formcontrol https://cngxjs.github.io/cngx/examples/#/forms/field/form-primitives/signal-forms-drop-the-atom-into-cngx-form-field

Metadata#

Host#

Providers#

CNGX_CHIP_GROUP_HOST
useExisting CngxChipGroup
CNGX_CONTROL_VALUE
useExisting CngxChipGroup
CNGX_FORM_FIELD_CONTROL
useExisting CngxChipGroup

Relationships

Index#

Inputs#

disabled#boolean
model()
default false
errorMessageId#string | null
input()

Optional id of an external error message element. When set, the host emits aria-errormessage="<id>"; consumers MUST render an element with that id. Default null skips the attribute. Note: WAI-ARIA dictates that AT ignores this attribute when aria-invalid is absent or "false", so a stable always-emitted id is harmless when the field is valid.

default null
invalid#boolean
model()

Bridge-writable invalid state. model<boolean> mirrors disabled so external integrations (RF/Signal-Forms bridges, custom validity adapters) can drive it without a parallel API path - consumers typically read only.

default false
label#string | undefined
input()
orientation#"horizontal" | "vertical"
input()
default 'horizontal'
required#boolean
model()
default false
selected#T | undefined
model()

Outputs#

disabled#boolean
model()
invalid#boolean
model()

Bridge-writable invalid state. model<boolean> mirrors disabled so external integrations (RF/Signal-Forms bridges, custom validity adapters) can drive it without a parallel API path - consumers typically read only.

required#boolean
model()
selected#T | undefined
model()

Instance Properties#

focused#unknown
Readonly
this.focusedState.asReadonly()
id#unknown
signal()Readonly
signal(nextUid('cngx-chip-group-')).asReadonly()
isDisabled#unknown
Readonly

CngxChipGroupHost - leaf-side cascade source.

this.disabled
value#unknown
Readonly
this.selected

Methods#

handleFocusIn#void
Protected
handleFocusOut#void
Protected
isSelected#boolean
isSelected(value: T)
@paramvalueT
remove#void
remove(value: T)
@paramvalueT
toggle#void
toggle(value: T)
@paramvalueT

HostBindings#

BindingExpression
[attr.id]id()
[attr.aria-label]label()
[attr.aria-disabled]disabled() ? "true" : null
[attr.aria-required]required() ? "true" : null
[attr.aria-invalid](invalid() || errorState()) ? "true" : null
[attr.aria-errormessage]errorMessageId()
[attr.aria-busy]ariaBusy() ? "true" : null
[class.cngx-chip-group--horizontal]orientation() === "horizontal"

HostListeners#

EventHandler
(focusin)focusin()
(focusout)focusout()

Default visuals for CngxChipGroup. The host carries .cngx-chip-group and frames a flex layout for projected <cngx-chip> children. Stacks vertically by default.

Variants

  • --horizontal - flips to a row with flex-wrap: wrap so chips fold to additional lines instead of overflowing

Inheritance

Gap cascades through the foundation:

  • --cngx-chip-group-gap -> --cngx-space-sm

Layout

--cngx-chip-group-gap#*
Default value 0.5rem

Gap between chip children. Falls back to --cngx-space-sm.