# Cascader

> Multi-level select. Drills through hierarchical categories one column at a time; picking a leaf commits the full path. A signal-forms native control — it implements `FormValueControl`, so `[formField]` binds straight to its `value` model (`readonly T[]`). `[(value)]` works standalone, and `[(ngModel)]` / reactive forms keep working through Angular's bridge.

Source: https://ngwr.dev/reference/components/cascader  
Kind: Component

## Import

```angular-ts
import { WrCascader, type WrCascaderOption } from 'ngwr/cascader';
```

## Basic

```angular-ts
<wr-cascader [options]="locations" [(value)]="picked" placeholder="Pick a location" />

locations: WrCascaderOption[] = [
  {
    value: 'us', label: 'United States', children: [
      { value: 'ca', label: 'California', children: [
        { value: 'la', label: 'Los Angeles' },
        { value: 'sf', label: 'San Francisco' },
      ] },
    ],
  },
  // ...
];
```

## changeOnSelect

Commit a path on every click, including parent nodes. Useful for filters where any level is meaningful.

```angular-html
<wr-cascader
  [options]="locations"
  [(value)]="picked"
  changeOnSelect
  placeholder="Pick any level"
/>
```

## API

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| `clearLabel` | Accessible name. Falls back to `select.clearSelection`, then `'Clear selection'`. | `string \| null` | `null` |
| `options`required | Root-level options. Each may have `children` for deeper levels. | `readonly WrCascaderOption<T>[]` | — |
| `placeholder` | Placeholder shown when no path is selected. | `string` | `''` |
| `ariaLabel` | Accessible name of the 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. | `string \| null` | `null` |
| `disabled` | Disable the cascader. Bound automatically from the field's disabled state when used with `[formField]`. | `boolean` | `false` |
| `readonly` | Refuse changes while the trigger stays focusable and the path still submits. Bound automatically from the field's readonly state when used with `[formField]`. The panel is where every edit happens, so a read-only cascader simply does not open — there is nothing to browse that is not already on the trigger — and the clear button goes away with it. Mirrored as `aria-readonly`, which role `combobox` supports. | `boolean` | `false` |
| `size` | Control size — shares the `--wr-control-*` contract. | `WrCascaderSize` | `'md'` |
| `clearable` | Show a clear-all (×) button on the trigger when a path is selected. | `boolean` | `true` |
| `changeOnSelect` | Allow selecting non-leaf (parent) nodes. When `false`, only leaves (nodes without children) commit a selection. | `boolean` | `false` |
| `separator` | Separator between labels in the trigger display. | `string` | `'/'` |
| `value` | Committed selection path (full array from root to leaf). Bound by `[formField]`, or two-way via `[(value)]`. | `unknown` | `[]` |
| `(touch)` | Emitted on blur / commit so a bound field can mark itself touched. | `void` | — |

## Types

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| `WrCascaderOption<T>` | `{ value: T, label: string, disabled?: boolean, children?: WrCascaderOption<T>[] }`. A node without `children` is a leaf. | `interface` | `—` |

## CSS custom properties

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| `--wr-cascader-col-width` | Width of each column in the panel. | `length` | `12rem` |
| `--wr-cascader-max-height` | Max height per column before scrolling. | `length` | `16rem` |

## CSS variables

Custom properties `ngwr/cascader` publishes. Each default below is declared on the component's own selector, so a `:root` override is shadowed by it — set them on that selector, on a wrapper you scope yourself, or inline on the element. Unlike the BEM class names, these are the supported way to restyle the component.

| Variable | Default | Declared on |
| --- | --- | --- |
| `--wr-cascader-bg` | `var(--wr-color-surface)` | `.wr-cascader` +1 variant override |
| `--wr-cascader-border` | `var(--wr-color-outline)` | `.wr-cascader` |
| `--wr-cascader-col-width` | `12rem` | `.wr-cascader` |
| `--wr-cascader-color` | `var(--wr-color-on-surface)` | `.wr-cascader` +1 variant override |
| `--wr-cascader-font-size` | `var(--wr-control-font-size-md)` | `.wr-cascader` +2 variant overrides |
| `--wr-cascader-line-height` | `var(--wr-control-line-height-md)` | `.wr-cascader` +2 variant overrides |
| `--wr-cascader-max-height` | `16rem` | `.wr-cascader` |
| `--wr-cascader-min-width` | `12rem` | `.wr-cascader` |
| `--wr-cascader-opt-font` | `var(--wr-control-font-size-md)` | `.wr-cascader-panel` +2 variant overrides |
| `--wr-cascader-opt-py` | `0.5rem` | `.wr-cascader-panel` +2 variant overrides |
| `--wr-cascader-padding-x` | `var(--wr-control-padding-x-md)` | `.wr-cascader` +2 variant overrides |
| `--wr-cascader-padding-y` | `var(--wr-control-padding-y-md)` | `.wr-cascader` +2 variant overrides |
| `--wr-cascader-radius` | `var(--wr-control-radius-md)` | `.wr-cascader` +2 variant overrides |
