CngxChipInput
projects/common/interactive/chip-input/chip-input.directive.ts
Import#
import { CngxChipInput } from '@cngx/common/interactive'
Description#
Tokenizing input that turns user-entered strings into emitted
tokenCreated events on separator keys (default: comma + Enter)
and on multi-token paste. Pairs naturally with
<cngx-multi-chip-group> or any consumer-managed chip list.
Validation slot - async-state producer. The directive provides
CNGX_STATEFUL via useExisting, exposing a
ManualAsyncState<string> slot driven by [validateToken]
invocations. Bridge directives (<cngx-toast-on />,
<cngx-banner-on />) auto-discover this slot through the
CNGX_STATEFUL token without an explicit [state] input wiring.
State machine: idle → pending → success | error per separator-key
invocation. Concurrent validations supersede via a monotonic
validationId - when a second token is entered before the first
resolves, the first's resolution is dropped and only the latest
outcome touches the slot. Mirrors createCommitController's
supersede contract.
Removal contract. (tokenRemoved) fires on Backspace at empty
input - semantically "user wants to remove the last chip". The
directive does not own the chip list; the consumer drops the last
entry from their state in response.
Duplicates. [existingTokens] is read on every separator-key
invocation; when allowDuplicates is false (default), a token
already present in the list is silently dropped. The directive
does not track a private "previously emitted" set - that would be
an unbounded memory leak. Consumers pass their canonical chip
list as [existingTokens].
Paste. Pasting text containing one or more single-character
separators (e.g. "red, green, blue") emits one tokenCreated
per non-empty fragment. Multi-char separators ('Enter') are
keyboard-only and ignored by the paste path. Single-token paste
(no embedded separator) is left to the default paste behaviour
so the input value populates normally and the user can decide.
<input
cngxChipInput
placeholder="Type a tag and press Enter"
[existingTokens]="tags()"
(tokenCreated)="addTag($event)"
(tokenRemoved)="popTag()"
[validateToken]="validateTag"
/>
<cngx-toast-on />Metadata#
Host#
Providers#
CNGX_STATEFUL- useExisting
CngxChipInput
Relationships
Depends on1
Index#
Properties
Methods
HostBindings
Inputs#
Outputs#
unknownInstance Properties#
CngxAsyncStateValidation slot - CngxStateful<string> contract surface. The
view is built from raw signals via buildAsyncStateView rather
than createManualState (which lives in @cngx/common/data)
because @cngx/common/data → @cngx/common/interactive already
exists via CngxSearch, and re-importing data from interactive
would create a circular package dependency. The 30-LOC inline
is acceptable scope-wise; a future refactor relocating
createManualState to @cngx/core/utils would let this
collapse to a single call.
buildAsyncStateView<string>({
status: this.statusState.asReadonly(),
data: this.dataState.asReadonly(),
error: this.errorState.asReadonly(),
progress: signal<number | undefined>(undefined).asReadonly(),
isFirstLoad: computed(() => !this.hadSuccess()),
lastUpdated: signal<Date | undefined>(undefined).asReadonly(),
})unknowninput<
((value: string) => Promise<string> | Observable<string>) | undefined
>(undefined)Methods#
HostBindings#
| Binding | Expression |
|---|---|
[attr.aria-busy] | isPending() ? "true" : null |
[attr.aria-invalid] | isInvalid() ? "true" : null |
HostListeners#
| Event | Handler |
|---|---|
(keydown) | keydown() |
(paste) | paste() |