
Фильтр выпадающим списком
Выпадающий список <select> — дальше селект — вместо чекбоксов собирается из двух своих чанков. Готового в поставке нет, но JS обрабатывает селект сам.
Задача
Вывести значения одного фильтра селектом, а остальные оставить чекбоксами. Обычно это нужно, когда значений много — например, производителей несколько десятков — и выбирают одно.
С селектом JS уже умеет:
- отправлять форму при выборе значения;
- сбрасывать селект кнопкой сброса формы;
- после фильтрации блокировать значения, по которым не осталось товаров;
- по кнопке «Назад» переключать селект на значение, выбранное раньше.
Решение
Чанк значения
Создайте чанк, например catalog.filter.item:
{* Ключи фильтров, которые выводятся селектом. Тот же набор — в чанке обёртки *}
{set $selectKeys = ['vendor']}
{if $key in $selectKeys}
<option value="{$value | esc}"{if $active} selected{/if}{if $disabled} disabled{/if}>{$label | esc}</option>
{else}
<label class="mfilter-item">
<input type="{if $multiple}checkbox{else}radio{/if}" name="{$key}{if $multiple}[]{/if}" value="{$value | esc}"{if $active} checked{/if}{if $disabled} disabled{/if}>
{$label | esc} <span class="mfilter-count">{$count}</span>
</label>
{/if}Чанк обёртки
Создайте чанк, например catalog.filter:
{set $selectKeys = ['vendor']}
<div data-filter-key="{$key}">
<div data-filter-label="{$label | esc}">{$label | esc}</div>
{if $key in $selectKeys}
<select name="{$key}">
{* Первая опция селекта — «ничего не выбрано» *}
<option value="">Все варианты</option>
{* Остальные опции селекта выводит чанк значения *}
{$items}
</select>
{else}
{$items}
{/if}
</div>Вызов
{'!mFilterForm' | snippet : [
'tpl' => 'catalog.filter',
'tplItem' => 'catalog.filter.item',
]}В примерах нет оформления — только то, без чего селект не заработает. Если у вас уже есть оформленный чанк обёртки, например копия стандартного mfilter.filter, добавьте в него ту же ветку {if $key in $selectKeys} вокруг {$items}. Атрибуты data-filter-key и data-filter-label у блока фильтра оставьте: по ним блок «Выбрано» находит название фильтра.
Все переменные обоих чанков — в разделе Чанки mFilterForm.
Почему два чанка
mFilterForm отрисовывает фильтр в два слоя: сначала каждое значение чанком значения, затем обёртку, куда значения попадают готовым HTML в $items. Поэтому опции селекта выводит чанк значения, а сам <select> — обёртка.
Чанк значения выбирается по типу фильтра, а не по ключу, и настройки «показывать селектом» в наборе фильтров нет:
| Тип фильтра | Чанк значения |
|---|---|
number | &tplSlider |
colors | &tplColor |
boolean | &tplBoolean |
| остальные | &tplItem |
Поэтому в $selectKeys можно добавлять только фильтры, значения которых идут через &tplItem.
Список ключей повторяется в обоих чанках: чанк значения отрисовывается раньше обёртки, и общих переменных у них нет.
Правила разметки
Без любого из этих правил селект отрисуется, но будет работать неправильно — и без ошибок в консоли.
name без []
{* Так *}
<select name="{$key}">
{* Не так *}
<select name="{$key}[]">Селект JS ищет строго по select[name="vendor"]. С vendor[] перестанут блокироваться пустые значения и не сработает «Назад». При открытии страницы по ссылке всё будет выглядеть исправно: выбранное значение отмечает сервер, а не JS.
Первая опция селекта — «Все варианты»
<select name="{$key}">
<option value="">Все варианты</option>
{$items}
</select>Эта опция селекта стоит в чанке обёртки, перед {$items}: чанк значения выводит только значения фильтра. Пустое значение снимает фильтр, и JS эту опцию селекта никогда не блокирует. Без неё выбор в селекте снимается только сбросом всей формы.
Одно значение
{* Не так *}
<select name="{$key}" multiple>Селект с multiple не поддерживается: по ссылке с несколькими значениями выбранным останется последнее. Если по фильтру выбирают несколько значений, уберите его ключ из $selectKeys в обоих чанках — фильтр снова выведется чекбоксами.
Число товаров в опции селекта
У чекбоксов JS после фильтрации обновляет число товаров, а у опций селекта только блокирует пустые. Число, выведенное в текст опции селекта, устареет после первого же выбора в соседнем фильтре.
Проще всего число не выводить — как в чанке значения выше.
Если число нужно, сохраните название в data-label:
<option value="{$value | esc}" data-label="{$label | esc}"{if $active} selected{/if}{if $disabled} disabled{/if}>{$label | esc} ({$count})</option>И обновляйте текст после каждой фильтрации — скрипт в любом месте страницы:
document.addEventListener('mfilter:success', (e) => {
const suggestions = e.detail.response.suggestions || {};
document.querySelectorAll('select option[data-label]').forEach((option) => {
const filter = suggestions[option.parentElement.name];
if (!filter) return;
const item = (filter.values || []).find((v) => String(v.value) === option.value);
option.textContent = `${option.dataset.label} (${item ? item.count : 0})`;
});
});Если не работает
| Симптом | Причина |
|---|---|
Внутри блока чекбоксов появились отдельные <option> | Ключ есть в $selectKeys чанка значения, но нет в обёртке |
| Селект пустой, а значения выведены чекбоксами рядом | Ключ есть в обёртке, но нет в чанке значения |
| Селект обернул цветные образцы или переключатель | В $selectKeys попал фильтр типа colors или boolean |
| Пустые значения не блокируются после фильтрации | name с [] |
| «Назад» не возвращает прежнее значение селекта | name с [] |
| Фильтр нельзя снять, не сбросив форму | В чанке обёртки нет опции селекта «Все варианты» |
| Число товаров в опции селекта не меняется после фильтрации | Число выведено в текст без скрипта обновления — см. «Число товаров в опции селекта» |
| Крестик в блоке «Выбрано» не снимает фильтр-селект | Ограничение JS: при снятии фильтра селект не сбрасывается и отправляет прежнее значение. То же при «Назад» на страницу, где фильтр не был выбран. Снимайте опцией селекта «Все варианты» или кнопкой сброса формы |
| Счётчик выбранных значений в заголовке фильтра гаснет после фильтрации | Ограничение JS: счётчик data-filter-badge считает только чекбоксы. У фильтра-селекта его не выводите |
