При работе с компонентами выбора интерфейса часто возникает необходимость структурировать большое количество значений. Простые списки становятся неудобными, если элементов много или если они логически разделяются на категории. Для решения этой задачи в Naive UI используется группировка опций.
Группировка позволяет объединять элементы выбора в визуальные и логические блоки. Это повышает читаемость интерфейса, облегчает поиск нужного значения и делает структуру данных более очевидной.
Наиболее активно группировка применяется в следующих компонентах:
n-selectn-cascadern-tree-selectn-transfern-auto-completeКаждый из этих компонентов поддерживает структурированные данные, где элементы могут объединяться в группы.
Группа представляет собой объект, содержащий заголовок и набор дочерних элементов. Базовая структура выглядит следующим образом:
const options = [
{
label: 'Фрукты',
type: 'group',
children: [
{ label: 'Яблоко', value: 'apple' },
{ label: 'Банан', value: 'banana' }
]
},
{
label: 'Овощи',
type: 'group',
children: [
{ label: 'Морковь', value: 'carrot' },
{ label: 'Картофель', value: 'potato' }
]
}
]
Ключевые поля:
| Поле | Назначение |
|---|---|
label |
название группы |
type |
тип элемента (group) |
children |
массив опций внутри группы |
Каждый элемент внутри children является обычной опцией
выбора.
Компонент n-select поддерживает группировку из коробки.
Достаточно передать массив опций со структурой групп.
<template>
<n-select
v-model:value="value"
:options="options"
placeholder="Выберите продукт"
/>
</template>
<script setup>
import { ref } from 'vue'
const value = ref(null)
const options = [
{
label: 'Фрукты',
type: 'group',
children: [
{ label: 'Яблоко', value: 'apple' },
{ label: 'Банан', value: 'banana' }
]
},
{
label: 'Овощи',
type: 'group',
children: [
{ label: 'Морковь', value: 'carrot' },
{ label: 'Картофель', value: 'potato' }
]
}
]
</script>
В раскрывающемся списке каждая категория будет отображаться с заголовком группы.
Визуальная структура:
Фрукты
Яблоко
Банан
Овощи
Морковь
Картофель
Группы обладают несколькими особенностями:
1. Невозможность выбора
Заголовок группы не является опцией выбора. Выбираются только
элементы внутри children.
2. Визуальное разделение
Naive UI автоматически добавляет отступы и стили, отделяющие группы друг от друга.
3. Поддержка фильтрации
При использовании поиска (filterable) фильтрация
применяется к дочерним элементам, но группы автоматически скрываются,
если внутри не осталось совпадений.
В реальных приложениях группы часто создаются динамически на основе данных с сервера.
Пример группировки пользователей по роли:
const users = [
{ name: 'Алексей', role: 'admin' },
{ name: 'Мария', role: 'admin' },
{ name: 'Иван', role: 'editor' },
{ name: 'Ольга', role: 'user' }
]
Формирование групп:
const groupedOptions = Object.entries(
users.reduce((acc, user) => {
if (!acc[user.role]) {
acc[user.role] = []
}
acc[user.role].push({
label: user.name,
value: user.name
})
return acc
}, {})
).map(([role, items]) => ({
label: role,
type: 'group',
children: items
}))
Получается структура:
admin
Алексей
Мария
editor
Иван
user
Ольга
Такой подход особенно полезен при работе с API.
Naive UI позволяет управлять структурой данных через параметры отображения полей. Это полезно, если данные приходят в нестандартном формате.
Например:
const options = [
{
title: 'Frontend',
items: [
{ name: 'Vue', id: 'vue' },
{ name: 'React', id: 'react' }
]
}
]
Настройка полей:
<n-select
:options="options"
:field-names="{
label: 'title',
children: 'items',
value: 'id'
}"
/>
Это позволяет адаптировать структуру данных без её преобразования.
Иногда требуется изменить внешний вид заголовка группы. В Naive UI
для этого используется слот
render-label.
Пример кастомного заголовка:
<n-select
:options="options"
:render-label="renderLabel"
/>
Функция рендера:
const renderLabel = (option) => {
if (option.type === 'group') {
return h(
'strong',
{ style: 'color: #666;' },
option.label
)
}
return option.label
}
В результате заголовки групп могут иметь собственный стиль, иконки или дополнительные элементы.
Часто категории сопровождаются визуальными маркерами.
Пример:
const options = [
{
label: 'Социальные сети',
type: 'group',
children: [
{ label: 'Telegram', value: 'telegram' },
{ label: 'Twitter', value: 'twitter' }
]
},
{
label: 'Мессенджеры',
type: 'group',
children: [
{ label: 'WhatsApp', value: 'whatsapp' },
{ label: 'Signal', value: 'signal' }
]
}
]
Кастомный рендер:
const renderLabel = (option) => {
if (option.type === 'group') {
return h('div', { class: 'group-label' }, [
h('span', { class: 'group-title' }, option.label)
])
}
return option.label
}
Такой подход используется для создания более насыщенных интерфейсов.
Компонент n-cascader строится на иерархии данных,
поэтому его структура похожа на группы.
Пример:
const options = [
{
label: 'Европа',
value: 'europe',
children: [
{ label: 'Франция', value: 'france' },
{ label: 'Германия', value: 'germany' }
]
},
{
label: 'Азия',
value: 'asia',
children: [
{ label: 'Япония', value: 'japan' },
{ label: 'Китай', value: 'china' }
]
}
]
Отличие от select заключается в том, что здесь элементы
могут иметь несколько уровней вложенности.
Группы хорошо сочетаются с режимом множественного выбора.
<n-select
v-model:value="value"
:options="options"
multiple
/>
Пользователь может выбирать элементы из разных групп одновременно.
Пример выбранных значений:
Фрукты → Яблоко
Овощи → Морковь
Naive UI корректно отображает выбранные элементы независимо от их групповой принадлежности.
Если список содержит сотни групп и тысячи элементов, необходимо учитывать производительность.
Практические рекомендации:
1. Использование виртуализации
<n-select
:options="options"
virtual-scroll
/>
Это позволяет рендерить только видимые элементы.
2. Ленивая загрузка данных
Группы можно загружать по мере необходимости.
3. Предварительная нормализация данных
Лучше формировать структуру групп один раз, а не на каждом рендере.
type: 'group'Если не указать тип группы, Naive UI будет воспринимать объект как обычную опцию.
Неправильно:
{
label: 'Фрукты',
children: [...]
}
Правильно:
{
label: 'Фрукты',
type: 'group',
children: [...]
}
value у
группыГруппы не должны иметь value.
Неправильно:
{
label: 'Фрукты',
value: 'fruits',
type: 'group',
children: [...]
}
Это может вызвать некорректное поведение выбора.
Нельзя одновременно использовать простые элементы и группы без логики структуры.
Лучше придерживаться единого формата:
группа
элементы
группа
элементы
const options = [
{
label: 'Frontend',
type: 'group',
children: [
{ label: 'JavaScript', value: 'js' },
{ label: 'TypeScript', value: 'ts' }
]
},
{
label: 'Backend',
type: 'group',
children: [
{ label: 'Node.js', value: 'node' },
{ label: 'Python', value: 'python' }
]
},
{
label: 'Mobile',
type: 'group',
children: [
{ label: 'Flutter', value: 'flutter' },
{ label: 'Kotlin', value: 'kotlin' }
]
}
]
Интерфейс выбора становится значительно понятнее, чем при использовании одного длинного списка.
Группировка опций является важным инструментом организации данных в компонентах Naive UI. Она повышает удобство интерфейсов, делает сложные списки структурированными и позволяет масштабировать элементы выбора при работе с большими наборами данных.