Skip to main content
cngx-src documentation

CngxButtonMultiToggleGroup

cngx-button-multi-toggle-group

ComponentPrimaryOnPushNo encapsulationv0.1.0WCAG AA

projects/common/interactive/button-toggle/button-multi-toggle-group.component.ts

Import#

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

Description#

Multi-select button-toggle group. Owns a selectedValues = model<T[]>([]) (the canonical multi-value source) and exposes the parent contract via CNGX_BUTTON_MULTI_TOGGLE_GROUP. Behaves as a W3C APG toolbar - arrow keys MOVE focus only (no auto-select); Space and Enter on a focused leaf toggle that leaf's membership.

Mode is static: this is the multi-select half of the deliberate single/multi split (per feedback_select_family_split). Consumers pick this component or <cngx-button-toggle-group> at template authoring time. Leaves (CngxButtonToggle) inject EITHER token with { optional: true } and choose aria-checked (single) vs aria-selected (multi) AT INJECTION TIME, never at runtime.

Internals lean on createSelectionController from @cngx/core/utils for membership tracking - the controller's stable per-value isSelected signals (memoised by key) let leaves read membership inside their own computed() without triggering a fresh array walk on every change-detection pass.

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.

<cngx-button-multi-toggle-group label="Filters" [(selectedValues)]="filters">
  <button cngxButtonToggle value="open">Open</button>
  <button cngxButtonToggle value="closed">Closed</button>
  <button cngxButtonToggle value="archived">Archived</button>
</cngx-button-multi-toggle-group>

https://cngxjs.github.io/cngx/examples/#/common/interactive/button-toggle/multi-group/basic-multi-filter-toolbar https://cngxjs.github.io/cngx/examples/#/common/interactive/button-toggle/multi-group/disabled-group-cascade-vs-per-toggle 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_BUTTON_MULTI_TOGGLE_GROUP
useExisting CngxButtonMultiToggleGroup
CNGX_CONTROL_VALUE
useExisting CngxButtonMultiToggleGroup
CNGX_FORM_FIELD_CONTROL
useExisting CngxButtonMultiToggleGroup

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
keyFn#(value: T) => unknown
default (v) => v
label#string | undefined
input()
orientation#"horizontal" | "vertical"
input()
default 'horizontal'
required#boolean
model()
default false
selectedValues#T[]
model()
default []

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()
selectedValues#T[]
model()

Instance Properties#

focused#unknown
Readonly
this.focusedState.asReadonly()
id#unknown
signal()Readonly
signal(nextUid('cngx-button-multi-toggle-group-')).asReadonly()
value#unknown
Readonly
this.selectedValues

Methods#

handleFocusIn#void
Protected
handleFocusOut#void
Protected
isSelected#Signal
isSelected(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-orientation]orientation()
[attr.aria-busy]ariaBusy() ? "true" : null
[class.cngx-button-multi-toggle-group--horizontal]orientation() === "horizontal"

HostListeners#

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

Default visuals for the shared button-toggle group container. The host carries either .cngx-button-toggle-group (single-select CngxButtonToggleGroup) or .cngx-button-multi-toggle-group (multi-select CngxButtonMultiToggleGroup). Children sit flush so the row reads as a segmented control.

Variants

  • --horizontal - flex-direction row (the default); a :not() rule on the scope root flips to column when this class is absent

Inheritance

Layout tokens cascade through the foundation:

  • --cngx-button-toggle-group-gap - direct (defaults to 0)
  • --cngx-button-toggle-group-radius -> --cngx-radius-md

Index#

Layout

--cngx-button-toggle-group-gap#*
Default value 0

Gap between toggle children. Zero by default so the buttons read as a segmented control.

--cngx-button-toggle-group-radius#*
Default value 0.375rem

Corner radius of the group container. Defaults to --cngx-radius-md.