import { Component } from '@angular/core';
import { WrTree } from 'ngwr/tree';
@Component({ imports: [WrTree] })
export class MyComponent {}Tree
A nested list to browse and select from, inline or as a dropdown picker.
Installation
Import the class, and @use the entry point in your global styles: without it the component renders unstyled.
Single selection
Bind [(selected)] and [(expanded)] to arrays of node ids.
<wr-tree
ariaLabel="Project files"
[nodes]="folders"
[(selected)]="picked"
[(expanded)]="open"
selectionMode="single"
/>Multi selection
A plain click replaces the selection; Cmd or Ctrl + click adds and removes a row.
<wr-tree
ariaLabel="Project files"
[nodes]="folders"
[(selected)]="picked"
selectionMode="multi"
/>Combobox mode — single
openOn="overlay" makes the tree a dropdown picker. Bind [(value)] or [formField].
<wr-tree
openOn="overlay"
[nodes]="folders"
selectionMode="single"
placeholder="Pick a folder"
[(value)]="picked"
/>Combobox mode — multi + chips
maxTagCount caps the chips shown before +N more. defaultExpandAll opens every parent on first open.
<wr-tree
openOn="overlay"
[nodes]="folders"
selectionMode="multi"
[maxTagCount]="2"
[defaultExpandAll]="true"
placeholder="Pick folders"
[(value)]="picked"
/>Combobox mode — width
A trigger left to size itself is 12rem wide. Change that with --wr-tree-trigger-min-width; a min-width on .wr-tree__trigger has no effect.
Virtual scroll
For a large tree, set virtualScroll and viewportHeight. rowHeight is measured when left at 0.
<wr-tree
ariaLabel="Generated folders"
[nodes]="bigTree"
[(expanded)]="expanded"
[(selected)]="picked"
virtualScroll
[viewportHeight]="320"
/>API
| Name | Description | Type | Default |
|---|---|---|---|
nodes | Tree data. | readonly WrTreeNode<TId>[] | [] |
selected | Selected node ids (two-way bindable). | readonly TId[] | [] |
expanded | Expanded node ids (two-way bindable). | readonly TId[] | [] |
selectionMode | Selection behavior. | WrTreeSelectionMode | 'single' |
disabled | Disable the whole tree. Bound automatically from the field's disabled state when used with [formField]. | boolean | false |
readonly | Refuse selection changes while the tree stays focusable and the value still submits. Bound automatically from the field's readonly state when used with [formField]. Expanding and collapsing keep working: a branch is NAVIGATION, not a value, so a read-only tree is still browsable — it is picking and un-picking that stops. In overlay mode the trigger additionally refuses to open, because there the panel exists only to choose from and it already shows what is chosen. aria-readonly rides on the combobox trigger only; role tree does not support the state, so the inline shape has nothing valid to mirror it onto. | boolean | false |
openOn | Render shape. | 'inline' | 'overlay' | 'inline' |
placeholder | Placeholder shown on the trigger when no node is selected. | string | '' |
ariaLabel | Accessible name of the overlay trigger. Falls back to the placeholder, then to select.label — a role="combobox" with nothing selected and no placeholder otherwise has no name at all. An INLINE tree puts it on the role="tree" itself, which ARIA requires to have a name — an aria-label on the <wr-tree> host never reaches it. Inside a <wr-form-field> the field's label names the tree instead. | string | null | null |
clearable | Show a clear-all (×) button on the trigger when at least one node is selected. | boolean | true |
maxTagCount | Cap on the number of chips rendered on the trigger before collapsing the rest into +N more (multi mode only). 0 = render every chip. | number | 0 |
defaultExpandAll | Auto-expand every node that has children on first open of the overlay. | boolean | false |
virtualScroll | Window the visible-node list so a large tree keeps only ~one viewport of rows in the DOM. Opt-in and OFF by default — a tree without it renders byte-identically to today. Works in both inline and overlay shapes; while on, keyboard navigation switches to the aria-activedescendant pattern so Arrow / Home / End / Enter keep working across un-rendered rows. | boolean | false |
rowHeight | Uniform row height in px used to map scroll offset to node index. 0 (default) measures the first rendered row once and reuses it, so it adapts to the active density / touch target automatically. Read only when virtualScroll is on. | number | 0 |
viewportHeight | Height of the scroll viewport when virtualScroll is on — a number (px) or any CSS length ('60vh'). A numeric px value lets the server prerender the exact first window. | number | string | 288 |
overscan | Extra rows kept rendered above and below the viewport as scroll headroom. | number | 6 |
panelClass | Extra CSS classes for the overlay pane, when openOn puts the tree in one. The pane is appended to the overlay container, not to this component, so nothing in the consumer's own template encloses it and no descendant rule written around the trigger can reach it. This input is the only per-instance handle on it. A space-separated string works as well as an array. Inline trees never open an overlay, so this input does nothing for the default openOn="inline" — the host element is right there to take a class of its own. | WrClassInput | null |
value | Form value — the current selection as seen by a bound field. Bound by [formField], or two-way via [(value)]. Shape follows selectionMode: TId | null in single mode, readonly TId[] in multi mode. Works in both openOn modes; [(selected)] stays the inline-native API and always carries an array, whatever the selection mode. | unknown | undefined |
(touch) | Emitted on blur so a bound field can mark itself touched. | void | — |
Types
| Name | Description | Type | Default |
|---|---|---|---|
WrTreeNode | { id, label, children?, disabled? }. id is what [(selected)] / [(expanded)] carry; a node without children is a leaf. | interface | — |
CSS variables
Set these on the component's selector or a wrapper, not on :root: the defaults are declared on ngwr/tree's own element.
| Variable | Default | Declared on |
|---|---|---|
--wr-tree-chip-font-size | var(--wr-text-sm) | .wr-tree |
--wr-tree-chip-gap | var(--wr-space-xs) | .wr-tree |
--wr-tree-chip-line-height | var(--wr-leading-snug) | .wr-tree |
--wr-tree-chip-padding-block | var(--wr-control-padding-y-sm) | .wr-tree |
--wr-tree-chip-padding-inline-end | var(--wr-space-xs) | .wr-tree |
--wr-tree-chip-padding-inline-start | var(--wr-space-sm) | .wr-tree |
--wr-tree-chips-gap | var(--wr-space-xs) | .wr-tree |
--wr-tree-clear-radius | var(--wr-border-radius-pill) | .wr-tree |
--wr-tree-font-family | var(--wr-font-family-base) | .wr-tree |
--wr-tree-font-size | var(--wr-text-sm) | .wr-tree |
--wr-tree-indent | 1.25rem | .wr-tree |
--wr-tree-list-padding-y | var(--wr-space-xs) | .wr-tree |
--wr-tree-overlay-padding-top | var(--wr-space-xs) | .wr-tree-overlay |
--wr-tree-panel-border | var(--wr-border-base) | .wr-tree-panel |
--wr-tree-panel-max-height | 18rem only under .wr-tree--combobox — unset elsewhere | .wr-tree--combobox |
--wr-tree-panel-padding | var(--wr-space-sm) | .wr-tree-panel |
--wr-tree-panel-radius | var(--wr-border-radius-base) | .wr-tree-panel |
--wr-tree-row-gap | var(--wr-space-sm) | .wr-tree |
--wr-tree-row-padding-x | var(--wr-space-sm) | .wr-tree |
--wr-tree-row-padding-y | var(--wr-space-xs) | .wr-tree +1 variant override |
--wr-tree-row-radius | var(--wr-border-radius-sm) | .wr-tree |
--wr-tree-row-selected-font-weight | var(--wr-font-weight-medium) | .wr-tree |
--wr-tree-toggle-radius | var(--wr-border-radius-sm) | .wr-tree |
--wr-tree-trigger-bg | var(--wr-color-surface) | .wr-tree +1 variant override |
--wr-tree-trigger-border | var(--wr-border-base) | .wr-tree |
--wr-tree-trigger-color | var(--wr-color-on-surface) | .wr-tree +1 variant override |
--wr-tree-trigger-font-family | var(--wr-font-family-base) | .wr-tree |
--wr-tree-trigger-font-size | var(--wr-control-font-size-md) | .wr-tree |
--wr-tree-trigger-gap | var(--wr-space-sm) | .wr-tree |
--wr-tree-trigger-line-height | var(--wr-control-line-height-md) | .wr-tree |
--wr-tree-trigger-min-width | 12rem only under .wr-tree--combobox — unset elsewhere | .wr-tree--combobox |
--wr-tree-trigger-multi-padding-x | var(--wr-space-sm) | .wr-tree |
--wr-tree-trigger-multi-padding-y | var(--wr-space-xs) | .wr-tree |
--wr-tree-trigger-padding-x | var(--wr-control-padding-x-md) | .wr-tree |
--wr-tree-trigger-padding-y | var(--wr-control-padding-y-md) | .wr-tree |
--wr-tree-trigger-radius | var(--wr-control-radius-md) | .wr-tree |
Dependencies
See also
Component CascaderPick a path through nested options, one column per level.Component SelectPick one or several values from a list, with search and free-text tags.Component TableRows of data with sorting, filtering, paging and custom cells.Component GraphDraws blocks joined parent to child, top to bottom, for display only.Component SidebarNavigation drawn from data: pages and groups, three levels deep.Component FormA plain layout wrapper for a label, a control and its error.Guide OverlayStyle ngwr's floating panels and control where focus goes.