CngxMultiChipGroup
cngx-multi-chip-group
projects/common/interactive/multi-chip-group/multi-chip-group.component.ts
Import#
import { CngxMultiChipGroup } from '@cngx/common/interactive'
Description#
Multi-select chip group. Owns a selectedValues = model<T[]>([])
(the canonical multi-value source) and exposes the parent contract
via CNGX_CHIP_GROUP_HOST. Behaves as a role="listbox" aria-multiselectable="true" 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
multi-select half of the chip-group split.
Internals lean on createSelectionController from @cngx/core/utils
for membership tracking - the controller's structural-equality
selected snapshot prevents downstream computed cascades from
thrashing when a re-emission produces the same logical contents
with a different array reference. Mirrors CngxCheckboxGroup's
controller wiring.
value is a structural alias of selectedValues so the group
satisfies CngxControlValue<T[]> without owning two synchronised
models - both names point to the same ModelSignal<T[]> instance.
[keyFn] lets consumers with object-typed values map each value
to a stable membership key (typically (v) => v.id). Without it,
membership uses identity / primitive equality.
[state] accepts CngxAsyncState<unknown> for async-loaded chip
lists; reactively drives aria-busy. 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: see CngxChipGroup JSDoc).
Roving strategy (accepted-debt §5). This molecule uses
CngxRovingTabindex (per the harmonized Phase 3-4 group precedent),
NOT createChipStripRoving. The strip-roving controller would solve
clamp-on-removal automatically but its tabindex contract requires
the parent to bind [attr.tabindex] per chip wrapper at the
template level - which a content-projected CngxChipInGroup leaf
model does not allow. Tracked in form-primitives-accepted-debt §5;
UX consequence is a one-shot focus reset to index 0 after
mid-strip removal, instead of clamping to the next valid sibling.
Removal flow (plan deviation). Plan revision claimed
createChipRemovalHandler from @cngx/forms/select would drive
the remove path. That import is a layer violation
(@cngx/common/interactive is Level 2; @cngx/forms/select is
Level 3). Removal here is a thin controller.deselect(value) -
the WeakMap-closure-cache machinery in the select-family handler
targets ngTemplateOutlet chip-slot stability, which content-
projected chip-in-group leaves do not exhibit.
Per Pillar 1 (Ableitung statt Verwaltung), aria-busy is a
computed() from state.status(). Per Pillar 3 (Komposition
statt Konfiguration), the group composes CngxRovingTabindex and
emits no implicit children - consumers project chip rows themselves.
<cngx-multi-chip-group label="Tags" [(selectedValues)]="tags">
@for (tag of options(); track tag) {
<cngx-chip cngxChipInGroup [value]="tag">{{ tag }}</cngx-chip>
}
</cngx-multi-chip-group>Metadata#
Host#
Providers#
CNGX_CHIP_GROUP_HOST- useExisting
CngxMultiChipGroup CNGX_CONTROL_VALUE- useExisting
CngxMultiChipGroup CNGX_FORM_FIELD_CONTROL- useExisting
CngxMultiChipGroup
Relationships
Index#
Properties
Derived State
HostBindings
Inputs#
string | nullOptional 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.
nullBridge-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.
false(value: T) => unknown(v) => vstring | undefinedOutputs#
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.
T[]Instance Properties#
unknownPublic membership count - useful for label hints + announcements.
this.controller.selectedCountMethods#
HostBindings#
| Binding | Expression |
|---|---|
[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-multi-chip-group--horizontal] | orientation() === "horizontal" |
HostListeners#
| Event | Handler |
|---|---|
(focusin) | focusin() |
(focusout) | focusout() |
Default visuals for CngxMultiChipGroup. The host carries
.cngx-multi-chip-group and frames a flex layout for projected
<cngx-chip> children in multi-select mode. Stacks vertically by
default. Gap cascades through the single-select sibling container
so spacing stays in sync across both group types unless a consumer
overrides one explicitly.
Variants
--horizontal- flips to a row withflex-wrap: wrapso chips fold to additional lines instead of overflowing
Inheritance
Gap chains through the sibling group then the foundation:
--cngx-multi-chip-group-gap->--cngx-chip-group-gap->--cngx-space-sm
Layout
*0.5remGap between chip children. Cascades through the single-select
group's --cngx-chip-group-gap and then --cngx-space-sm.