Селект позволяет выбрать одно или несколько значений из предопределенного списка.

При единичном выборе значения контрол сброса скрыт. Его отображение можно включить отдельно.

Многострочное поле растёт с каждой выбранной опцией, поэтому при длинном выборе становится выше самого выпадающего списка. Если в этот момент панель не помещается ни под полем, ни над ним, она прижимается к первой строке поля и рисуется поверх остальных, открываясь на отступ ниже этой строки. Первая строка, шеврон и клинер остаются видимыми и кликабельными.

При множественном выборе разметку каждого тега можно заменить своей. Объявите внутри <kbq-select> шаблон <ng-template #kbqSelectTagContent> — он разворачивается для каждой выбранной опции и получает саму опцию как $implicit, а экземпляр KbqSelect — как select:

<ng-template #kbqSelectTagContent let-option let-select="select">
    <kbq-tag [selectable]="false" [disabled]="option.disabled || select.disabled">
        {{ option.viewValue }}
        @if (!option.disabled && !select.disabled) {
            <i kbq-icon="kbq-xmark-s_16" kbqTagRemove (click)="select.onRemoveMatcherItem(option, $event)"></i>
        }
    </kbq-tag>
</ng-template>

Шаблон полностью замещает встроенную разметку, поэтому цвет, неактивное состояние и элемент удаления нужно воспроизвести самостоятельно. Корневым элементом должен остаться <kbq-tag>: счетчик скрытых тегов «+N» измеряет отрисованные элементы kbq-tag, и любой другой корневой элемент его ломает.

Используйте поиск, когда в списке больше 10 элементов. Поиск разбивает многословный запрос на части и ищет их независимо друг от друга, удаляет пробелы по краям, не зависит от регистра и сворачивает диакритические знаки. Алгоритм работы описан в руководстве «Умный поиск».

В режиме мультивыбора есть возможность выбрать все варианты сразу. Эта функция отключена по умолчанию — включите её атрибутом selectAll, и над списком появится мастер-чекбокс.

<kbq-select multiple selectAll placeholder="Placeholder">
    <kbq-option [value]="option">{{ option }}</kbq-option>
</kbq-select>

У чекбокса три состояния: снят, если ничего не выбрано, промежуточный, если выбрана только часть опций, и выбран, если выбраны все. Клик по промежуточному состоянию выбирает оставшиеся опции, а не снимает выделение. Подпись берётся из локали (select.selectAll).

Неактивные (disabled) опции игнорируются: они не выбираются и не снимаются, а состояние чекбокса отражает только те опции, которые пользователь действительно может переключить. При поиске чекбокс действует только на результаты, попавшие под запрос. Каждое действие по строке порождает одно событие selectionChange на всю пачку, после которого следует onSelectAll.

Не поддерживается вместе с withVirtualScroll и showPreselectedValues: строка не отображается, если включено хотя бы одно из них, — «выбрать все» способен действовать только на опции, отрисованные как KbqOption, а не на весь виртуализированный или заранее выбранный набор данных.

Когда выбраны все, контрол может показывать особую подпись, а не буквально перечислять выбранные опции. Спроецируйте <kbq-select-trigger>, пока allOptionsSelected равно true, — без него селект вернётся к триггеру по умолчанию. Используйте именно элемент (или элемент с атрибутом kbq-select-trigger), а не <ng-container>: триггер растягивается на всю ширину контрола правилом, привязанным к этому элементу, а у ng-container элемента нет — и тогда подпись со стрелкой окажутся прижаты друг к другу.

Ctrl/Cmd + A выбирает все опции при множественном выборе. По умолчанию повторное нажатие оставляет их выбранными; атрибут selectAllToggle заставляет его снимать выделение. При включённом selectAll сочетание всегда работает как переключатель — так оно не может разойтись с мастер-чекбоксом.

Внутри непустого поля поиска первое нажатие выделяет текст поля, следующее переходит к опциям. Поведение можно полностью заменить через инпут selectAllHandler — тогда onSelectAll для сочетания клавиш не эмитится, поведением распоряжается обработчик.

При малом количестве опций поиск может автоматически скрываться. По умолчанию такая возможность отключена.

Для настройки используйте атрибут searchMinOptionsThreshold со значением:

  • auto. Поиск будет скрыт, если количество опций меньше 10. (поведение по умолчанию).
  • <число>. Выключение поиска, если количество опций меньше указанного числа.

Используйте kbqSelectOptionsProvider, чтобы настроить все поля в модуле по единым правилам:

import { kbqSelectOptionsProvider } from '@koobiq/components/select';

@NgModule({
    providers: [
        kbqSelectOptionsProvider({ searchMinOptionsThreshold: 'auto' })
    ]
})

В нижнем колонтитуле можно разместить вспомогательные элементы: действия, ссылки, подсказки. Он остаётся на месте, пока список прокручивается под ним.

Действие оформляется строкой выпадающего меню, а не кнопкой из формы: добавьте kbq-select-footer-item на нативный button или a, и оно встанет ровно под опциями. Любой клик внутри колонтитула закрывает панель.

При открытой панели Tab переводит фокус на содержимое колонтитула — по очереди на каждый интерактивный элемент, будь то строка действия или ссылка, а Shift + Tab проходит их в обратном порядке. Выключенные элементы пропускаются: и с атрибутом disabled, и с классом kbq-disabled, который приходится использовать ссылке. Когда элементы кончаются, следующий Tab закрывает панель и возвращает фокус на поле — как и Esc из любого места колонтитула.

По умолчанию максимальная высота спиcка равна 256px. Когда вариантов выбора много, в выпадающем меню появляется прокрутка.

При необходимости можно настроить высоту. Например, в обычном меню видно 7–8 элементов. Если предполагается выбор из 10 опций, то можно увеличить высоту списка и показать все элементы, не скрывая малую часть под скроллом. Для этого используйте атрибут panelMaxHeight со значением в пикселях.

panelMaxHeight ограничивает прокручиваемый список опций. Строка поиска и нижний колонтитул находятся рядом со списком, поэтому они добавляются к общей высоте панели. Значение больше оставшегося места в видимой области будет обрезано оверлеем, а не прокручено. При использовании cdk-virtual-scroll-viewport значение задаёт точную высоту, а не потолок: виртуальному скроллеру нужна определённая высота.

Чтобы задать высоту для всех селектов в модуле с общими правилами отображения, используйте kbqSelectOptionsProvider.

import { kbqSelectOptionsProvider } from '@koobiq/components/select';

@NgModule({
    providers: [
        kbqSelectOptionsProvider({ panelMaxHeight: 400 })
    ]
})

Для темизации та же высота доступна через токен --kbq-select-panel-size-max-height: задайте его на :root, чтобы изменить все панели сразу, или на классе, переданном через panelClass, — чтобы изменить одну. В примере выше показаны оба способа.

Ширина выпадающего списка равна селекту и она увеличивается, когда в списке есть длинный текст.

Минимальная ширина выпадающего списка равна 200px для аккуратного отображения рядом с узким селектом. При необходимости минимальную ширину можно изменить. Для этого можно использовать атрибут panelMinWidth и передать в него числовое значение.

Для настройки минимальной ширины выпадающих списков всех селектов внутри модуля, имеющих единые правила отображения, можно использовать провайдер kbqSelectOptionsProvider.

import { kbqSelectOptionsProvider } from '@koobiq/components/select';

@NgModule({
    providers: [
        kbqSelectOptionsProvider({ panelMinWidth: 350 })
    ]
})

Выпадающий список можно настроить так, чтобы его ширина совпадала с полем. Для этого используйте атрибут panelWidth со значением auto. При этом список не станет меньше panelMinWidth — задайте panelMinWidth равным 0, если список должен точно повторять узкое поле.

Чтобы задать фиксированную ширину 400 px, используйте атрибут panelWidth со значением 400. Фиксированная ширина используется как есть, поэтому panelMinWidth к ней не применяется.

Рост по содержимому останавливается на 640 px. Ограничение мягкое: оно не делает список меньше ширины поля и не уменьшает явно заданный panelWidth. Изменить его для одного поля можно атрибутом panelMaxWidth, а для всего приложения — задав токен --kbq-panel-size-width-max на :root (он общий с Tree-select, Autocomplete и Dropdown).

Для настройки ширины выпадающих списков всех селектов внутри модуля, имеющих единые правила отображения, можно использовать провайдер kbqSelectOptionsProvider.

import { kbqSelectOptionsProvider } from '@koobiq/components/select';

@NgModule({
    providers: [
        kbqSelectOptionsProvider({ panelWidth: 'auto' })
    ]
})

Селект может содержать уже заранее выбранные значения.

Выбранные элементы можно переместить в начало списка.

Добавьте cdk-virtual-scroll-viewport в шаблон компонента, чтобы отображать только видимые элементы и улучшить производительность.

Если в качестве значений опций используются объекты, задайте virtualOptionFactory — функцию, которая по значению возвращает KbqVirtualOption с подписью (и при необходимости с per-value disabled). Фабрика срабатывает, когда KbqOption выбранного значения не отрендерен: его выкинул виртуальный скролл из viewport, значение установлено программно до рендера соответствующей опции или значения нет в загруженных данных (серверный поиск). Полученный KbqVirtualOption используется и для подписи в триггере одиночного селекта, и для тегов в режиме мультивыбора.

Фильтрация источника данных — например, строкой поиска по тому же массиву — сохраняет выбранное значение в триггере, даже пока его опция отфильтрована из списка.

По умолчанию выпадающее меню скрывается под горизонтальный Navbar и Topbar, в остальных случаях показывается поверх соседних элементов.

Чтобы меню не перекрывало нужный вам элемент при прокрутке, а скрывалось под ним, настройте положение через кастомный z-index или параметры смещения.

  • Если вы используете селект без лейбла, то советуем добавить placeholder для указания, какую информацию пользователь должен выбрать. Например, «Страна».
  • Если предполагается более 10 вариантов выбора, то включите поиск в выпадающем меню.
Предложения по улучшению
Если вы нашли ошибку или хотите доработать статью, создайте запрос на GitHub.