| name | n-tree-select |
| description | A tree-structured select component for hierarchical data selection with features like multiple selection, checkboxes, filtering, and async loading |
| author | jiaiyan |
| version | 1.0.0 |
n-tree-select Component
The n-tree-select component combines the functionality of a select dropdown with a tree structure, allowing users to select values from hierarchical data. It's ideal for scenarios like organization charts, file system navigation, category selection, and any nested data structure.
When to Use
Use n-tree-select when you need to:
- Hierarchical Selection: Select values from nested/hierarchical data structures
- Category Selection: Choose categories from a multi-level category tree
- Organization Picker: Select departments or teams from an organizational hierarchy
- File/Folder Selection: Navigate and select files from a folder structure
- Multi-Level Filtering: Filter and select from nested option groups
Basic Usage
Basic Tree Select
<template>
<n-tree-select
:options="options"
default-value="Drive My Car"
@update:value="handleUpdateValue"
/>
</template>
<script setup>
import { ref } from 'vue'
const options = [
{
label: 'Rubber Soul',
key: 'Rubber Soul',
children: [
{ label: 'Drive My Car', key: 'Drive My Car' },
{ label: 'Norwegian Wood', key: 'Norwegian Wood' }
]
},
{
label: 'Let It Be',
key: 'Let It Be',
children: [
{ label: 'Dig It', key: 'Dig It' },
{ label: 'Let It Be', key: 'Let It Be' }
]
}
]
const handleUpdateValue = (value, option) => {
console.log(value, option)
}
</script>
Multiple Selection
<template>
<n-tree-select
multiple
:options="options"
:default-value="['Norwegian Wood']"
@update:value="handleUpdateValue"
/>
</template>
Checkable with Cascade
<template>
<n-tree-select
multiple
cascade
checkable
:options="options"
:default-value="['Norwegian Wood']"
/>
</template>
Filterable
<template>
<n-space vertical>
<n-tree-select
filterable
:options="options"
default-value="Drive My Car"
clearable
/>
<n-tree-select
multiple
checkable
filterable
:clear-filter-after-select="false"
:options="options"
:default-value="['Norwegian Wood']"
clearable
/>
</n-space>
</template>
Show Line
<template>
<n-tree-select
show-line
default-expand-all
:options="options"
default-value="Drive My Car"
/>
</template>
API Reference
Props
| Name | Type | Default | Description |
|---|
value / v-model:value | string | number | Array<string | number> | null | undefined | Selected key(s). Use array for multiple selection. |
default-value | string | number | Array<string | number> | null | null | Default uncontrolled value. |
options | TreeSelectOption[] | [] | Tree options data. |
multiple | boolean | false | Allow multiple selections. |
checkable | boolean | false | Show checkboxes for selection. |
cascade | boolean | false | Link parent and child selection. |
check-strategy | 'all' | 'parent' | 'child' | 'all' | How to display selected nodes when both parent and children are selected. |
filterable | boolean | false | Enable search filtering. |
filter | (pattern: string, option: TreeSelectOption) => boolean | - | Custom filter function. |
clearable | boolean | false | Allow clearing selection. |
clear-filter-after-select | boolean | true | Clear filter keyword after selection in multiple mode. |
disabled | boolean | |
TreeSelectOption Properties
| Name | Type | Description |
|---|
key | string | number | Unique key for the option. |
label | string | Display label. |
children | TreeSelectOption[] | Child options. |
disabled | boolean | Whether the option is disabled. |
isLeaf | boolean | Whether the node is a leaf (required for async loading). |
Events
| Name | Parameters | Description |
|---|
update:value | (value: string | number | Array<string | number> | null, option: TreeSelectOption | null | Array<TreeSelectOption | null>, meta: { node: TreeOption | null, action: 'select' | 'unselect' | 'delete' | 'clear' }) => void | Emitted when value changes. |
update:expanded-keys | (value: Array<string | number>, meta: { node: TreeOption | null, action: 'expand' | 'collapse' | 'filter' }) => void | Emitted when expanded keys change. |
update:indeterminate-keys | (keys: Array<string | number>) => void | Emitted when indeterminate keys change. |
focus | (e: FocusEvent) => void | Emitted on focus. |
blur | (e: FocusEvent) => void | Emitted on blur. |
load | (node: TreeSelectOption) => Promise<void> | Async data loading callback. |
Slots
| Name | Parameters | Description |
|---|
header | () | Header content in the menu. |
action | () | Action content at the bottom of the menu. |
arrow | () | Custom arrow icon. |
empty | () | Empty state content. |
Methods
| Name | Type | Description |
|---|
focus | () => void | Focus the component. |
blur | () => void | Blur the component. |
focusInput | () => void | Focus the input element. |
blurInput | () => void | Blur the input element. |
getCheckedData | () => { keys: Array<string | number>, options: Array<TreeOption | null> } | Get checked data. |
getIndeterminateData | () => { keys: Array<string | number>, options: Array<TreeOption | null> } | Get indeterminate data. |
Common Patterns
Async Loading
<template>
<n-tree-select
v-model:value="value"
multiple
checkable
:options="options"
:cascade="cascade"
:check-strategy="checkStrategy"
:show-path="showPath"
:allow-checking-not-loaded="cascade"
:on-load="handleLoad"
/>
</template>
<script setup>
import { ref } from 'vue'
const value = ref([])
const options = ref([
{
label: 'Root',
key: 'root',
children: [
{ label: 'Loading...', key: 'child1', isLeaf: false }
]
}
])
const handleLoad = async (node) => {
const children = await fetchChildren(node.key)
node.children = children
}
</script>
Custom Field Names
<template>
<n-tree-select
:options="options"
default-value="Drive My Car"
label-field="whateverLabel"
key-field="whateverKey"
children-field="whateverChildren"
/>
</template>
<script setup>
const options = [
{
whateverLabel: 'Album',
whateverKey: 'album',
whateverChildren: [
{ whateverLabel: 'Song 1', whateverKey: 'song1' }
]
}
]
</script>
Check Strategy Demo
<template>
<n-space vertical>
<n-radio-group v-model:value="checkStrategy">
<n-radio-button value="all">All</n-radio-button>
<n-radio-button value="parent">Parent</n-radio-button>
<n-radio-button value="child">Child</n-radio-button>
</n-radio-group>
<n-tree-select
multiple
cascade
checkable
:check-strategy="checkStrategy"
:options="options"
:default-value="['Dig It', 'go']"
/>
</n-space>
</template>
File Picker Simulation
<template>
<n-tree-select
block-line
:options="fileOptions"
:override-default-node-click-behavior="override"
/>
</template>
<script setup>
const override = ({ option }) => {
if (option.isFolder) {
return 'toggleExpand'
}
return 'toggleSelect'
}
const fileOptions = [
{
label: 'Documents',
key: 'docs',
isFolder: true,
children: [
{ label: 'report.pdf', key: 'report', isFolder: false }
]
}
]
</script>
Validation Status
<template>
<n-space vertical>
<n-tree-select status="warning" placeholder="Warning state" />
<n-tree-select status="error" placeholder="Error state" />
</n-space>
</template>
Custom Rendering
<template>
<n-tree-select
:options="options"
:render-label="renderLabel"
:render-prefix="renderPrefix"
/>
</template>
<script setup>
import { h } from 'vue'
const renderLabel = ({ option, checked, selected }) => {
return h('span', { style: { fontWeight: selected ? 'bold' : 'normal' } }, option.label)
}
const renderPrefix = ({ option }) => {
return h('span', option.children ? '📁' : '📄')
}
const options = [
{
label: 'Folder',
key: 'folder',
children: [
{ label: 'File', key: 'file' }
]
}
]
</script>
Best Practices
Performance
-
Virtual Scroll: Keep virtual-scroll enabled (default) for large tree structures.
-
Lazy Loading: Use async loading with on-load for deeply nested trees to improve initial load time.
-
Limit Expanded Nodes: Avoid default-expand-all for very large trees; use default-expanded-keys instead.
User Experience
-
Check Strategy: Choose appropriate check-strategy:
'all': Show all selected nodes (default)
'parent': Show only parent when all children selected
'child': Show only leaf nodes
-
Show Path: Enable show-path for deep hierarchies to help users understand the selection context.
-
Filterable: Enable filterable for trees with many nodes to help users find options quickly.
-
Clearable: Enable clearable to allow users to easily reset their selection.
Data Handling
-
Unique Keys: Ensure each option has a unique key property.
-
isLeaf Property: Set isLeaf: false for nodes that will have children loaded asynchronously.
-
Custom Field Names: Use key-field, label-field, and children-field when your data doesn't match the default structure.
-
Cascade Selection: Be careful with cascade and allow-checking-not-loaded when using async loading, as values may be incomplete.
Form Integration
-
Validation Status: Use the status prop to show validation feedback.
-
Disabled State: Use disabled to prevent selection in read-only scenarios.
-
Loading State: Show loading state during async operations.