Группировка опций

При работе с компонентами выбора интерфейса часто возникает необходимость структурировать большое количество значений. Простые списки становятся неудобными, если элементов много или если они логически разделяются на категории. Для решения этой задачи в Naive UI используется группировка опций.

Группировка позволяет объединять элементы выбора в визуальные и логические блоки. Это повышает читаемость интерфейса, облегчает поиск нужного значения и делает структуру данных более очевидной.

Наиболее активно группировка применяется в следующих компонентах:

  • n-select
  • n-cascader
  • n-tree-select
  • n-transfer
  • n-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 является обычной опцией выбора.


Использование групп в компоненте Select

Компонент 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.


Группировка с использованием option groups 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
}

Такой подход используется для создания более насыщенных интерфейсов.


Группировка в Cascader

Компонент 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. Она повышает удобство интерфейсов, делает сложные списки структурированными и позволяет масштабировать элементы выбора при работе с большими наборами данных.