Import#
import { CngxTab } from '@cngx/common/tabs'
Description#
Single-tab atom.
Registers with the enclosing presenter via
CNGX_TAB_GROUP_HOST on construction; deregisters via
DestroyRef on teardown.
The presenter holds the atom's
input signals by reference, so input changes propagate without
re-registration. labelTemplate / contentTemplate are projected
by the organism - the atom carries no rendering logic.
<div cngxTab [label]="'Settings'">
... content templates here ...
</div>Metadata#
Index#
Methods
Derived State
Inputs#
boolean | undefinedPer-tab close-affordance override. undefined (default) inherits
the group-level [closable] resolution; set false to pin this
tab open inside a dismissable group, or true to make a single tab
closable while the group default is off.
string | booleanDirect error flag for the common "this tab is invalid" case - no
errorAggregator boilerplate required. true or a non-empty string
drives the error state; a string doubles as the inline message
(surfaced via the *cngxTabErrorBadge slot context). false / ''
clear it. The aggregator stays the rich multi-source forms path; the
two channels compose (either errors). Mirrors CngxStep.error.
falseCngxErrorAggregatorContract | undefinedstring | undefinedstring | undefinedOptional secondary text line under the primary label (a count, a
status word, a short detail). Convenience for the common text
case, parallel to [label]; the *cngxTabSubLabel slot covers
richer content. Folds into the tab's accessible name.
Instance Properties#
Methods#
CngxTab
The single-tab atom. It renders nothing itself - its inputs are the source of the tab's place in the accessibility tree, and the organism renders them into ARIA. Get the inputs right and the a11y follows.
- Selection is derived from a single source.
selectedreads the presenter'sactiveId, never a stored flag, soaria-selectedand the roving tab stop cannot drift apart. The active tab is the group's loneTabstop; arrow keys move within the tablist. label(plussubLabel) is the accessible name. Both fold into the spoken name, and the organism prefixes position ("Tab 2 of 5: Settings"). Keep them meaningful;subLabelis read on every announcement, so avoid restating the label.- Error state is communicated, not just painted.
[error](or theerrorAggregator) driveshasError, which lights the error badge and fills the tab'saria-describedbySR descriptor. A non-empty[error]string doubles as that spoken message. The badge and colour are never the only channel. - Deferred-reveal validation stays silent. The aggregator arm reads
shouldShow, nothasError, so form errors are not announced before the user reaches and reveals them - AT is not spammed with pending validation. The direct[error]flag has no reveal concept: it shows the moment the consumer sets it. disabledis skipped, not removed. A disabled tab carriesaria-disabledand is skipped by arrow-key navigation, but stays in the tablist and the "of N" count for context.idanchors the wiring. The bound[id]is the stable key behind keyboard focus targeting (data-tab-id), the tabpanel association, and the(tabClose)callback - so a skin reorder cannot misdirect focus or labelling.
When you reach for it
Almost never directly. This entry is consumed by the skin packages - you import from @cngx/ui/tabs or @cngx/ui/mat-tabs for ready-to-use components.
You touch this entry when:
- You are building a custom rendering for tabs (a fully bespoke design system) and want the CNGX keyboard, ARIA, commit lifecycle, and overflow behaviour without redoing them.
- You are wiring tab-level slot defaults (busy spinner, error badge, overflow trigger) across the application via the configuration cascade.
- You need to read the per-tab contract from inside a sub-component - host tokens, not concrete classes.
Mental model
Tabs are a presenter applied as a hostDirective, a set of declarative CngxTab definitions, content projection via CngxTabContent / CngxTabLabel, and a configurable overflow surface. The skin component is thin - it forwards inputs and renders a
CNGX_TAB_GROUP_HOST and CNGX_TAB_PANEL_HOST.
Two things make this layout work:
- Commit lifecycle. Activating a tab can fire an async action with optimistic or pessimistic semantics. The active state rolls back if the action fails. Per-tab busy and error markers ride the same channel.
- Overflow. When tabs do not fit, the runaway tabs collapse into a "more" trigger. Overflow recompute runs on an IntersectionObserver with a debounce, so resize storms do not thrash the layout.