Skip to main content
cngx-src documentation

CngxFilterChips

ComponentPrimaryOnPushv0.1.0WCAG AA

projects/common/data/filter-chips/filter-chips.component.ts

Import#

import { CngxFilterChips } from '@cngx/common/data'

Description#

Bridge that connects a multi-select chip strip to a CngxFilter predicate via Pillar-1 derivation - no effect() write-back, no manual sync. The bridge composes <cngx-multi-chip-group> for the visible chip strip and registers a single closure-style predicate on the parent [filterRef]. Chip toggles update selectedValues; the registered closure reads selectedValues() lazily on every invocation so downstream filtered consumers recompute without the bridge ever re-registering.

Filter chips are inherently multi-select per feedback_select_family_split; a single-mode filter-chip pattern is intentionally out of scope. Consumers needing a single-select filter wire <cngx-chip-group> to a custom predicate themselves.

Sync contract - derivation, not effect. The bridge calls filterRef.addPredicate(filterKey, predicateFn) EXACTLY ONCE during mount (in afterNextRender, because input.required() cannot be read from the constructor body). predicateFn is a closure that reads this.selectedValues() and this.optionValueFn() on every invocation. Because CngxFilter.predicate is itself a computed() consumed by downstream filtered lists, each item-evaluation flows through selectedValues() in the consumer's reactive context - chip toggles propagate without any effect() writing back into the filter.

Empty-selection semantics. When selectedValues() is [], the closure returns true for every item - equivalent to "no filter applied". The bridge deliberately does NOT call removePredicate on empty selection: leaving the predicate registered with a constant-true short-circuit is cheaper than re-registering on every empty/non-empty boundary, and keeps the predicate-stack stable (no predicatesChange emission churn).

Teardown. DestroyRef.onDestroy calls removePredicate only when registration actually happened (registered flag) - guards against the case where the component is destroyed before afterNextRender fires. The destroy callback reads filterRef and filterKey lazily so a consumer that re-binds these between mount and unmount cleans the latest binding (per reference_signal_architecture memory-hygiene rule).

<cngx-filter-chips
  label="Tags"
  [options]="tagOptions"
  optionLabel="label"
  optionValue="id"
  [filterRef]="employees"
  filterKey="tags"
/>

<ng-container cngxFilter #employees="cngxFilter">
  @for (emp of filteredEmployees(); track emp.id) {
    ...
  }
</ng-container>

https://cngxjs.github.io/cngx/examples/#/common/data/filter-chips/custom-chip-decoration-via-cngxfilterchip https://cngxjs.github.io/cngx/examples/#/common/data/filter-chips/multi-role-filter-wired-to-a-list

Metadata#

Relationships

Index#

Inputs#

disabled#boolean
model()

Form-state surface forwarded transparently to the inner <cngx-multi-chip-group>. Each is a model<boolean> matching the inner shape so consumers can two-way bind with their own forms state without reaching past the bridge. Mirrors CngxCheckboxGroup / CngxButtonToggleGroup precedent.

default false
errorMessageId#string | null
input()
default null
filterKey#string
input()Required
filterRef#CngxFilter
Required
invalid#boolean
model()
default false
keyFn#(value: TValue) => unknown

Membership key extractor for object-valued chip values. When TValue is an object (e.g. { id, label } rather than a primitive id), this fn extracts a stable identity key so chip membership survives re-emissions with fresh references. Forwarded to the inner <cngx-multi-chip-group> AND used inside the predicate's Object.is comparison. Defaults to identity - works for primitive values.

default (v) => v
label#string
input()Required
optionLabel#(item: TItem) => string
Required
options#readonly TItem[]
input()Required
optionValue#(item: TItem) => TValue
Required
orientation#"horizontal" | "vertical"
input()
default 'horizontal'
required#boolean
model()
default false
selectedValues#TValue[]
model()

Internal multi-select state driven by chip toggles. Exposed as a model so consumers may seed initial selection via [(selectedValues)] if they need to; usually the consumer leaves this internal and reads results via the parent CngxFilter.predicate.

default []

Outputs#

disabled#boolean
model()

Form-state surface forwarded transparently to the inner <cngx-multi-chip-group>. Each is a model<boolean> matching the inner shape so consumers can two-way bind with their own forms state without reaching past the bridge. Mirrors CngxCheckboxGroup / CngxButtonToggleGroup precedent.

invalid#boolean
model()
required#boolean
model()
selectedValues#TValue[]
model()

Internal multi-select state driven by chip toggles. Exposed as a model so consumers may seed initial selection via [(selectedValues)] if they need to; usually the consumer leaves this internal and reads results via the parent CngxFilter.predicate.

Instance Properties#

chipContext#unknown
ProtectedReadonly

Build the slot context for a given chip option. optionValue is invoked here per option for the chip rendering pass; the predicate closure invokes it again per LIST item for membership filtering. Both invocations are necessary because option items (the chip pool) and filtered list items (the consumer's filtered data) are separate data paths - they happen to share a shape under the Phase-5 single-shape limitation, but the bridge cannot memoise across the two paths without conflating them. Consumers should keep optionValue cheap (typically a property lookup); a future [itemValue] input will separate the two extractors.

() => {...}
chipTemplate#Signal
contentChild()ProtectedReadonly

Resolved consumer-supplied chip template. The query uses an instantiation expression (CngxFilterChip<TItem, TValue>) so the bridge's class generics propagate into the slot directive type; absent that propagation, contentChild(CngxFilterChip) returns Signal<CngxFilterChip<unknown, unknown> | undefined> and the chipContext() helper would have to widen on every call.

contentChild(
  CngxFilterChip<TItem, TValue>,
)