API reference
Attach tngListbox to the owned list container and tngOption to each option host. The directives handle selection state, roving focus, and typeahead without imposing a wrapper DOM.
Focus stays on the tngListbox host. Individual tngOption items are exposed to assistive technology and active-descendant tracking, but they are not part of the normal Tab sequence.
tngListbox
Primitive attachment
html
<div
tngListbox
tabindex="0"
[multiple]="true"
[value]="selectedChannels()"
(valueChange)="onSelectedChannelsChange($event)"
>
@for (channel of channels; track channel.id) {
<div
tngOption
[tngValue]="channel.id"
[disabled]="channel.disabled === true"
>
{{ channel.label }}
</div>
}
</div>
| Input / output | Type | Notes |
|---|---|---|
multiple | boolean | Enables multi-selection. Default is single selection. |
orientation | 'vertical' | 'horizontal' | Controls the arrow-key axis for active-item roving. |
direction | 'ltr' | 'rtl' | Used when orientation="horizontal". |
disabled | boolean | Disables the entire listbox interaction model. |
loop | boolean | Wraps roving focus from last to first option and vice versa. |
typeahead | boolean | Printable keys jump to matching option text. Enabled by default. |
value / valueChange | T | readonly T[] | null | Selection model for single or multiple mode. |
tngOption
| Input | Type | Notes |
|---|---|---|
tngValue | T | Value committed into the listbox selection model. |
disabled | boolean | Prevents pointer and keyboard selection for that option. |
Reflected attributes
The directives expose state through ARIA and presence-based data attributes so you can style without relying on implementation-private classes.
Reflected attributes
html
<div
role="listbox"
aria-activedescendant="tng-option-..."
aria-multiselectable="true"
>
<div
role="option"
tabindex="-1"
aria-selected="true"
data-active
data-selected
>
Docs
</div>
</div>
| Directive | Attributes | Purpose |
|---|---|---|
tngListbox | role, aria-multiselectable, aria-disabled, aria-activedescendant | Listbox semantics and active-descendant wiring. |
tngOption | role, tabindex="-1", aria-selected, aria-disabled | Option semantics for screen readers while the listbox host owns Tab focus. |
tngOption | data-active, data-selected, data-disabled | Public styling hooks for stateful option rendering. |
Change handling
In multiple mode, normalize the emitted value into an array so summaries and downstream filters stay stable.
Selection normalizer
ts
type ListboxValue = string | readonly string[] | null;
readonly selectedChannels = signal<readonly string[]>(['docs']);
onSelectedChannelsChange(value: ListboxValue): void {
if (value === null) {
this.selectedChannels.set([]);
return;
}
this.selectedChannels.set(typeof value === 'string' ? [value] : value);
}