Составной компонент для фильтрации данных в таблице или списке.
В режиме очистки фильтры остаются на панели после нажатии на кнопку с крестиком, а в режиме удаления они пропадают. Не смешивайте два режима на одном экране. Если у соседних фильтров кнопки с иконкой крестика будут работают по‑разному, то это запутает пользователя.
Используйте, когда в системе предусмотрено мало фильтров или пользователи часто используют несколько популярных параметров. Фильтр-кнопки без выбранных значений заранее показаны на экране, заполненный фильтр очищается по крестику, но сама кнопка не пропадает.
В режиме очистки не стоит давать возможность добавить новые фильтры из меню, так как появится конфликт с тем, что крестик будет то сбрасывать, то удалять фильтр.
В этом режиме работы у пользователя есть возможность скрыть компонент, если в фильтре не выбрано значений. В заполненном состоянии кнопка с крестиком сбрасывает значения фильтра и скрывает его. Фильтры с возможностью удаления обычно скрыты в меню, чтобы не перегружать экран. Пользователь выберет нужный параметр из выпадающего списка, и после этого соответствующий элемент будет добавлен на панель фильтрации.
В панель фильтров независимо от режима работы можно добавить фильтр, который всегда будет заполнен и который нельзя очистить. Пользователь сможет только изменить его значение.
Значение фильтра нельзя изменить, фильтр можно только удалить.
По клику на псевдоссылку в списке параметров в фильтр будет добавлено новое значение. Если фильтра нет в меню или в панели: то будет добавлен неактивный элемент, значение которого нельзя изменить, можно только удалить.
Для типов select и multiselect в шаблоне пайпа доступен необязательный compareWith — компаратор, который пробрасывается во внутренний select и определяет, как выбранное значение сопоставляется со списком опций. Переопределите его, когда опции сравниваются по бизнес-ключу или когда выбранное значение — это отдельный объект (например, восстановленный из сохранённого фильтра), а не та же ссылка. Если не задан, опции сопоставляются по их id. Кастомный компаратор сам отвечает за обработку null/undefined — в отличие от компаратора по умолчанию, который никогда не считает null/undefined совпадением.
Тип input — единственный без поповера: он отображает обычное поле ввода, в котором печатают прямо в баре, а name пайпа используется как плейсхолдер. Значение применяется по ходу ввода — через 200 мс после последнего нажатия клавиши (debounceTime) и начиная с 3 символов (minLength), — а не через кнопку «Применить». Текст короче порога, в том числе очищенное поле, применяется как null, поэтому бар никогда не продолжает фильтровать по тексту, которого уже нет в поле. Enter и потеря фокуса применяют значение сразу и порог игнорируют: явное действие пользователя применяется как введено. Единственный способ очистки — встроенный клинер внутри поля, поэтому removable ни на что не влияет: пайп типа input можно удалить только программно. Значения обрезаются по краям, а пустое значение применяется как null. Ширина задаётся CSS-переменной --kbq-filter-bar-pipe-input-width.
В шаблонах пайпов date и datetime доступны необязательные ограничения произвольного периода. minDateTime и maxDateTime задают границы выбираемого диапазона: пайп datetime использует полный момент (дату и время), пайп date — только день. minInterval и maxInterval ограничивают длину выбранного периода (end − start) и задаются объектом длительности (например, { days: 3 }, { hours: 1 }); период вне этих границ показывает ошибку и блокирует «Применить», а само ограничение форматируется под текущую локаль. Все четыре параметра игнорируются другими типами пайпов.
Если в фильтре много значений, то полезно включить поиск в выпадающем меню.
Если много значений, «Выбрать все» позволит одним действием выбрать все значения, либо снять выделение со всех. При поиске, мастер-чекбокс выбирает только результаты, попавшие под запрос.
В шаблонах пайпов multiselect и multi-tree-select доступен необязательный параметр lockedValues — список значений, выбор с которых нельзя снять. Такие значения всегда выбраны: они отображаются как недоступные опции с отмеченным чекбоксом, их не снимает очистка пайпа, а если во входящем значении фильтра их нет — они дописываются молча, без события изменения. Пайп, в котором выбраны только неснимаемые значения, считается пустым: он отображается как незаполненный, а кнопка очистки скрыта. «Считается пустым» здесь относится только к отображению: value пайпа применяется как есть, поэтому очистка убирает лишь то, что пользователь мог выбрать сам, а onClearPipe и onChangePipe по-прежнему отдают value, в котором неснимаемые значения перечислены.
«Выбрать все» такие значения полностью игнорирует: их нельзя ни выбрать, ни снять этим действием, а общий чекбокс отражает только те опции, которые пользователь реально может переключать. При этом под selectedAllEqualsSelectedNothing полный выбор по-прежнему применяется как пустое value — неснимаемые значения там подразумеваются, а не перечисляются.
Элементы lockedValues имеют ту же форму, что и элементы value соответствующего пайпа: для multiselect — объекты вида { name, id } (сопоставляются через compareWith, а без него — по id), для multi-tree-select — сырые значения узлов дерева. Неснимаемый узел-ветка блокирует всё своё поддерево. Параметр игнорируется остальными типами пайпов.
Поскольку неснимаемые опции отображаются как disabled, клавиатурная навигация их пропускает.
По умолчанию высота выпадающего списка пайпов select, multiselect, tree-select и multi-tree-select равна 256px — это ровно восемь опций по 32px, поэтому список из восьми опций помещается без прокрутки. Чтобы изменить высоту, передайте в шаблоне пайпа panelMaxHeight — значение в пикселях:
pipeTemplates: KbqPipeTemplate[] = [ { name: 'Select', type: KbqPipeTypes.Select, values: [/* ... */], panelMaxHeight: 160, cleanable: false, removable: false, disabled: false } ];
Значение ограничивает только прокручиваемый список, причём собственные отступы списка добавляются сверх него — поэтому значение, кратное высоте опции (32px), помещается без прокрутки и без обрезанной строки. Поле поиска отрисовывается над списком и добавляется к общей высоте панели, а строка «Выбрать все» прокручивается вместе с опциями и входит в ограничение. Значение больше, чем осталось места в видимой области, обрезается оверлеем, а не прокручивается. Если параметр не задан — или передан null — применяется значение по умолчанию. Параметр игнорируется остальными типами пайпов.
Опция с подписью и любая опция при multilineOptions выше 32px — их помещается меньше.
Добавьте к значению опции пайпа select или multiselect свойство caption, чтобы показать в выпадающем списке вторую строку — например, технический идентификатор.
По умолчанию обе строки обрезаются; multilineOptions: true включает перенос.
В раскрывающийся список можно добавить возможность поиска объектов с незаданным значением какого-либо параметра. Элемент располагается в начале списка и локализуется. Предусмотрена возможность указать собственную формулировку, чтобы она согласовывалась с названием фильтра.
Не показывайте фильтр, когда значений для выбора заведомо нет. Если фильтр скрыть невозможно, по клику должна отображаться подсказка.
Подсказка заменяет список пайпа select, multiselect, tree-select или multi-tree-select, в шаблоне которого нет значений (values); поле поиска и «Выбрать все» скрываются. Текст берётся из локализации — чтобы согласовать его с названием фильтра, передайте noOptionsText в шаблоне пайпа. Параметр игнорируется остальными типами пайпов.
Значение фильтра нельзя изменить, а также удалить.
Текстовый поиск позволяет искать информацию по любым данным, даже если для них нет отдельных фильтров.
Поиск разбивает многословный запрос на части и ищет их независимо друг от друга, удаляет пробелы по краям, не зависит от регистра и сворачивает диакритические знаки. Алгоритм работы описан в руководстве «Умный поиск».
Пользователь может быстро получить результаты поиска, выбрав сохраненный фильтр, без повторной настройки параметров. Также можно создать новый набор фильтров и использовать в будущем.
filter-bar запоминает, какой фильтр выбран и какие изменения в него внесли, и восстанавливает состояние после перезагрузки страницы. Включено по умолчанию — используйте [useStateSaving]="false" для отключения у компонента.
Это не то же самое, о чём раздел выше. Там пользователь сохраняет именованный фильтр, и хранит его приложение; здесь компонент хранит состояние на устройстве.
Восстановление пишет через модель filter, поэтому перекрывает значение, поданное привязкой [filter] на инициализации, и поднимает filterChange до того, как пользователь что-либо трогал. Выигрывает только изменение, сделанное после этого.
Проецируйте пайпы из того значения, которое отдаёт компонент: привяжите [(filter)] или присвойте то, что приходит в (filterChange). Восстановление заменяет объект фильтра и каждый его пайп — ровно так же, как это делает выбор фильтра в <kbq-filters>, — поэтому пайп, спроецированный из собственного массива, это уже не тот пайп, что лежит в filter: его правка не доходит до сохранённого состояния, а кнопка удаления перестаёт срабатывать.
Значения возвращаются новыми объектами — ровно для этого и нужен compareWith, см. Типы фильтров. Фильтр опознаётся по name, поэтому тот, которого больше нет в filters, не восстанавливается. Пайп собирается заново из фильтра или из pipeTemplates, поэтому пайп, который приложение собрало само, минуя оба источника, не восстанавливается — положите его в pipeTemplates, если он должен возвращаться.
clearSavedState() удаляет сохранённое, hasSavedState говорит, есть ли оно. Ни то ни другое не связано с saveFilterState() / restoreFilterState(), которые снимают копию фильтра в памяти в пределах одной сессии.
Ключи, хранилище и срок жизни записи одинаковы у всех компонентов, которые сохраняются, — см. Сохранение состояния компонентов.
Собственные строки фильтр-бара — меню фильтров, кнопка сброса, тултипы пайпов, произвольный период у пайпа с датой — берутся из KbqLocaleService. Данные, которые вы передаёте в pipeTemplates и filter, бар не переводит: их подписи — ваша зона ответственности. В примере ниже подписи периодов у пайпа datetime выводятся из активной локали через DateFormatter.duration(), поэтому следуют за локалью своего бара. Список вариантов следует за pipeTemplates, поэтому новый список переименовывает его на месте. С подписью, сохранённой в значении самого пайпа, так не выйдет: *kbqPipe создаёт компонент пайпа один раз по переданному объекту и последующие изменения этой привязки игнорирует, поэтому примеры ниже собирают свои фильтры заново.
Управлять локалью можно тремя способами. KBQ_DEFAULT_LOCALE_ID — это запасное значение (ru-RU), которое используется, если ничего не задано; это обычная константа, а не токен, поэтому предоставить её через DI нельзя. KBQ_LOCALE_ID задаёт локаль один раз, в момент создания KbqLocaleService, и его нужно предоставлять вместе с самим сервисом: сервис читает токен из того инжектора, который его создал. KbqLocaleService.setLocale() меняет локаль в рантайме.
Каждый фильтр-бар ниже предоставляет свой экземпляр KbqLocaleService, поэтому они изолированы друг от друга и от переключателя языка в футере сайта.