The Nested Select component allows users to select options from a hierarchical tree structure with expandable categories and subcategories.
Basic usage
<flx-nested-select
[options]="nestedOptions"
[(ngModel)]="selectedValue">
</flx-nested-select>
Properties
Hierarchical array of options
placeholder
string
default:"Select an option"
Placeholder text when nothing is selected
Enable search/filter functionality
Allow expanding/collapsing of categories
Show full path of selected item (e.g., “Category > Subcategory > Item”)
NestedOption interface
interface NestedOption {
value: any;
label: string;
icon?: string;
disabled?: boolean;
children?: NestedOption[];
}
Events
Emitted when selection changes
expand
EventEmitter<NestedOption>
Emitted when a category is expanded
collapse
EventEmitter<NestedOption>
Emitted when a category is collapsed
Examples
Basic nested select
With icons
Organization hierarchy
With search
Disabled options
Reactive forms
<flx-nested-select
label="Category"
[(ngModel)]="selectedCategory"
[options]="categories">
</flx-nested-select>
export class CategorySelector {
selectedCategory: string;
categories = [
{
value: 'electronics',
label: 'Electronics',
children: [
{
value: 'computers',
label: 'Computers',
children: [
{ value: 'laptops', label: 'Laptops' },
{ value: 'desktops', label: 'Desktops' },
{ value: 'tablets', label: 'Tablets' }
]
},
{
value: 'phones',
label: 'Phones',
children: [
{ value: 'smartphones', label: 'Smartphones' },
{ value: 'feature-phones', label: 'Feature Phones' }
]
}
]
},
{
value: 'clothing',
label: 'Clothing',
children: [
{
value: 'mens',
label: "Men's Clothing",
children: [
{ value: 'shirts', label: 'Shirts' },
{ value: 'pants', label: 'Pants' },
{ value: 'shoes', label: 'Shoes' }
]
},
{
value: 'womens',
label: "Women's Clothing",
children: [
{ value: 'dresses', label: 'Dresses' },
{ value: 'tops', label: 'Tops' },
{ value: 'skirts', label: 'Skirts' }
]
}
]
}
];
}
<flx-nested-select
[(ngModel)]="selectedLocation"
[options]="locations">
</flx-nested-select>
export class LocationSelector {
selectedLocation: string;
locations = [
{
value: 'north-america',
label: 'North America',
icon: 'globe',
children: [
{
value: 'usa',
label: 'United States',
icon: 'flag',
children: [
{ value: 'california', label: 'California', icon: 'map-pin' },
{ value: 'new-york', label: 'New York', icon: 'map-pin' },
{ value: 'texas', label: 'Texas', icon: 'map-pin' }
]
},
{
value: 'canada',
label: 'Canada',
icon: 'flag',
children: [
{ value: 'ontario', label: 'Ontario', icon: 'map-pin' },
{ value: 'quebec', label: 'Quebec', icon: 'map-pin' }
]
}
]
}
];
}
<flx-nested-select
label="Department"
[(ngModel)]="selectedDept"
[options]="orgStructure"
[showPath]="true">
</flx-nested-select>
<p *ngIf="selectedDept">
Selected: {{ getFullPath(selectedDept) }}
</p>
export class OrgSelector {
selectedDept: string;
orgStructure = [
{
value: 'engineering',
label: 'Engineering',
children: [
{
value: 'frontend',
label: 'Frontend',
children: [
{ value: 'react-team', label: 'React Team' },
{ value: 'angular-team', label: 'Angular Team' }
]
},
{
value: 'backend',
label: 'Backend',
children: [
{ value: 'api-team', label: 'API Team' },
{ value: 'data-team', label: 'Data Team' }
]
}
]
},
{
value: 'sales',
label: 'Sales',
children: [
{ value: 'enterprise', label: 'Enterprise Sales' },
{ value: 'smb', label: 'SMB Sales' }
]
}
];
getFullPath(value: string): string {
// Implementation to get full path from nested structure
return 'Engineering > Frontend > React Team';
}
}
<flx-nested-select
label="Search products"
[(ngModel)]="selectedProduct"
[options]="products"
[searchable]="true"
placeholder="Search or browse categories...">
</flx-nested-select>
Search filters across all levels of the hierarchy and highlights matching results.
<flx-nested-select
[(ngModel)]="selectedOption"
[options]="optionsWithDisabled">
</flx-nested-select>
export class DisabledExample {
selectedOption: string;
optionsWithDisabled = [
{
value: 'available',
label: 'Available Category',
children: [
{ value: 'option1', label: 'Option 1' },
{ value: 'option2', label: 'Option 2', disabled: true },
{ value: 'option3', label: 'Option 3' }
]
},
{
value: 'unavailable',
label: 'Unavailable Category',
disabled: true,
children: [
{ value: 'option4', label: 'Option 4' }
]
}
];
}
<form [formGroup]="filterForm">
<flx-nested-select
label="Product category"
formControlName="category"
[options]="categories">
</flx-nested-select>
</form>
import { FormBuilder, Validators } from '@angular/forms';
export class FilterForm {
filterForm = this.fb.group({
category: ['', Validators.required]
});
categories = [
// Nested options
];
constructor(private fb: FormBuilder) {}
}
Styling
flx-nested-select {
--flx-nested-select-indent: 20px;
--flx-nested-select-item-padding: 8px 12px;
--flx-nested-select-hover-background: #f3f4f6;
--flx-nested-select-selected-background: #dbeafe;
--flx-nested-select-expand-icon-size: 16px;
--flx-nested-select-max-height: 400px;
}
Accessibility
The Nested Select component ensures accessibility:
role="tree" with role="treeitem" for hierarchy
- Arrow keys for navigation (up/down, left/right for expand/collapse)
aria-expanded for expandable items
aria-selected for selected items
- Keyboard shortcuts (Enter to select, Space to expand)
- Screen reader announcements
Best practices
- Keep hierarchy depth to 3-4 levels maximum
- Use clear, descriptive labels at each level
- Enable search for large hierarchies
- Show the full path of selected items for clarity
- Group related items logically
- Consider alphabetical or logical ordering
- Provide visual feedback for expandable items
- Allow keyboard navigation
For deep hierarchies with many options, enable searchable to help users find items quickly without navigating through multiple levels.