Skip to main content

Overview

The CascadeSelect component provides a dropdown interface for selecting values from hierarchical data structures. It supports nested option groups, keyboard navigation, and extensive customization.

Component Selector

<magary-cascade-select />

Basic Usage

import { Component } from '@angular/core';
import { MagaryCascadeSelect } from 'ng-magary';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-demo',
  standalone: true,
  imports: [MagaryCascadeSelect, FormsModule],
  template: `
    <magary-cascade-select
      [(ngModel)]="selectedValue"
      [options]="categories"
    />
  `
})
export class DemoComponent {
  selectedValue: any = null;
  
  categories = [
    {
      label: 'Electronics',
      children: [
        { label: 'Computers', value: 'computers' },
        { label: 'Phones', value: 'phones' }
      ]
    },
    {
      label: 'Clothing',
      children: [
        { label: 'Shirts', value: 'shirts' },
        { label: 'Pants', value: 'pants' }
      ]
    }
  ];
}

Custom Label and Value

<magary-cascade-select
  [(ngModel)]="selectedId"
  [options]="locations"
  [optionLabel]="'name'"
  [optionValue]="'id'"
  [optionGroupChildren]="['subdivisions']"
/>
locations = [
  {
    name: 'United States',
    subdivisions: [
      { name: 'California', id: 'CA' },
      { name: 'New York', id: 'NY' }
    ]
  }
];

Selectable Groups

<magary-cascade-select
  [(ngModel)]="selectedValue"
  [options]="categories"
  [optionGroupSelectable]="true"
/>

With Error State

<magary-cascade-select
  [(ngModel)]="selectedValue"
  [options]="options"
  [invalid]="true"
  [error]="'Please select a category'"
/>

Properties

Data

options
Record<string, unknown>[]
default:"[]"
Array of option objects representing the hierarchical data structure
optionLabel
string
default:"'label'"
Property name to use as the display label for each option
optionValue
string | null
default:"null"
Property name to use as the value. If null, the entire option object is used as the value
optionGroupLabel
string[] | string | null
default:"null"
Property name(s) for group labels. If not specified, uses optionLabel
optionGroupChildren
string[]
default:"['children']"
Array of property names to check for child options. First matching property is used
optionGroupSelectable
boolean
default:"false"
When true, allows selecting parent groups in addition to leaf nodes

Display

placeholder
string
default:"'Select an option'"
Placeholder text shown when no value is selected
size
'small' | 'normal' | 'large'
default:"'normal'"
Size of the component
width
string
default:"'100%'"
Width of the component

State

disabled
boolean
default:"false"
When true, disables the component
loading
boolean
default:"false"
When true, shows loading state
invalid
boolean
default:"false"
When true, applies error styling

Validation & Help

error
string
default:"''"
Error message to display below the component
helpText
string
default:"''"
Help text to display below the component

Form Integration

The CascadeSelect component implements ControlValueAccessor and works with Angular forms.

Reactive Forms

import { FormControl, ReactiveFormsModule } from '@angular/forms';

category = new FormControl<string | null>(null);
<magary-cascade-select
  [formControl]="category"
  [options]="categories"
/>

Keyboard Navigation

KeyAction
Enter / SpaceOpen/close dropdown or select active option
Arrow DownMove to next option (opens if closed)
Arrow UpMove to previous option (opens if closed)
Arrow RightExpand active group
Arrow LeftCollapse to parent group
HomeMove to first option
EndMove to last option
EscapeClose dropdown
TabClose dropdown and move focus

Accessibility Features

  • ARIA combobox pattern with listbox popup
  • Keyboard navigation support
  • Screen reader announcements
  • Focus management
  • Disabled and loading state handling
  • Error and help text associations

Styling

The CascadeSelect uses the following structure:
  • .magary-cascade-select: Main container
  • .magary-cascade-select-trigger: Trigger button
  • .magary-cascade-select-panel: Dropdown panel
  • .magary-cascade-select-option: Individual option
  • .magary-cascade-select-group: Group option
  • .selected: Applied to selected option
  • .active: Applied to keyboard-active option

Complete Example

import { Component } from '@angular/core';
import { MagaryCascadeSelect } from 'ng-magary';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';

interface Category {
  label: string;
  value?: string;
  children?: Category[];
}

@Component({
  selector: 'app-cascade-demo',
  standalone: true,
  imports: [MagaryCascadeSelect, ReactiveFormsModule, CommonModule],
  template: `
    <div class="demo-container">
      <h3>Product Categories</h3>
      <magary-cascade-select
        [formControl]="category"
        [options]="categories"
        placeholder="Select a category"
      />
      <p *ngIf="category.value">
        Selected: {{ category.value }}
      </p>

      <h3>Location Selector</h3>
      <magary-cascade-select
        [formControl]="location"
        [options]="locations"
        [optionLabel]="'name'"
        [optionValue]="'code'"
        [optionGroupChildren]="['states']"
        placeholder="Select a location"
      />

      <h3>With Selectable Groups</h3>
      <magary-cascade-select
        [formControl]="department"
        [options]="departments"
        [optionGroupSelectable]="true"
        placeholder="Select department or team"
      />

      <h3>With Error</h3>
      <magary-cascade-select
        [formControl]="required"
        [options]="categories"
        [invalid]="required.touched && !required.value"
        [error]="'This field is required'"
        placeholder="Required field"
      />
    </div>
  `
})
export class CascadeSelectDemoComponent {
  category = new FormControl<string | null>(null);
  location = new FormControl<string | null>(null);
  department = new FormControl<string | null>(null);
  required = new FormControl<string | null>(null);

  categories: Category[] = [
    {
      label: 'Electronics',
      children: [
        {
          label: 'Computers',
          children: [
            { label: 'Laptops', value: 'laptops' },
            { label: 'Desktops', value: 'desktops' }
          ]
        },
        { label: 'Phones', value: 'phones' }
      ]
    },
    {
      label: 'Clothing',
      children: [
        { label: 'Men', value: 'men' },
        { label: 'Women', value: 'women' }
      ]
    }
  ];

  locations = [
    {
      name: 'United States',
      code: 'US',
      states: [
        { name: 'California', code: 'CA' },
        { name: 'New York', code: 'NY' }
      ]
    }
  ];

  departments: Category[] = [
    {
      label: 'Engineering',
      value: 'eng',
      children: [
        { label: 'Frontend', value: 'eng-fe' },
        { label: 'Backend', value: 'eng-be' }
      ]
    }
  ];
}

Build docs developers (and LLMs) love