Взаимозависимые фильтры через HTML-виджет

Часто возникает необходимость фильтровать значения одного системного виджета фильтра в зависимости от значений другого. К примеру, при выборе региона отображать только относящиеся к нему достопримечательности. На момент актуальной версии AW 1.25 реализовать такой функционал можно через HTML-виджет и функцию setFilter. Рассмотрим на примере.

Подготовка данных для фильтров

В первую очередь нам необходимо получить в модели маппинг для значений фильтров. Это можно сделать через новую промежуточную модель в которой выведем уникальные строки в виде «Регион» - «Тип достопримечательности». При необходимости использования других показателей или их количества принцип остается тем же. Пример данных для модели фильтрации представлен на рисунке. После создание модели не забудем загрузить данные в хранилище.

Создание HTML-виджета с фильтрами

Теперь создадим на этой модели виджет с типом HTML. В «Столбцы» добавим поля «Регион», «Достопримечательность»(1) без применения агрегации для получения плоской таблицы(2).

Теперь перейдем непосредственно к коду. В первую очередь создадим div-контейнеры и select-элементы на вкладке HTML.

код HTML
<div class="filters-container">
    <select class="filter region-filter">
    </select>
    <select class="filter attr-filter">
    </select>
</div>

Также добавим базовую стилизацию на вкладке CSS.

код CSS
.filters-container {
    width: 100%;
    height: 100%;
}

.filter {
    border-radius: 12px;
    flex: 1;
    padding: 0 16px 0 6px;
    overflow: hidden;
    color: black;
    min-width: 45%;
    max-width: 50%;
    min-height: 30px;
    box-shadow: 0px 5px 10px -3px rgba(0,0,0,0.1);
}

.filter option {
    color: black;
}

Осталось самое интересное: добавить JS код, где будем заполнять значения фильтров и добавим логику их применения. Данные для фильтров будут доступны в объекте window.DATA.data в виде массива объектов с полями, добавленным в столбцы.

Общий алгоритм для вкладки JS следующий:

  1. При первой отрисовке виджета необходимо получить списки для значений каждого из фильтров и иерархический маппинг между ними.
  2. Для списков значений сгенерировать объекты option и добавить в соответствующий select
  3. К полученным фильтрам добавить обработчик, в котором вызывать функцию setFilter, которая отфильтрует данные модели.

Пример реализации представлен ниже. Необходимо учесть названия полей для фильтров и адаптировать их под свои названия в коде.

код JS
// функция для генерации <option> элементов и их добавление в соответствующий <select>
const initOptions = (data, selectElement) => {
    for (let item of data) {
        const option = document.createElement('option');
        option.value = item;
        option.innerHTML = item;
        selectElement.appendChild(option);
    }
}

// объект для маппинга регион-достопримечательность
const regionAttrMap = {}

// обработка фильтрации достопримечательностей
const attrFilter = document.querySelector('.attr-filter');
attrFilter.addEventListener('change', (e) => {
    setFilter('att_name', e.target.value, '=');
})

// обработка фильтрации регионов
const regionFilter = document.querySelector('.region-filter');
regionFilter.addEventListener('change', (e) => {
    attrFilter.innerHTML = '';
    initOptions(regionAttrMap[e.target.value], attrFilter);
    setFilter('region', e.target.value, '=');
    setFilter('att_name', '', '=');
})

// замыкание, чтобы фильтры заполнились только 1 раз при первом рендере
let init = true;
function render() {
    // заполняем начальное состояние фильтров
    if (init) {
        // уникальные значения для фильтра по региону
        let regions = DATA.data.map(item => item.region.value).filter(x => x);
        regions = Array.from(new Set(regions));
        // уникальные значения для фильтра по достопримечательностям
        let attrs = DATA.data.map(item => item.att_name.value).filter(x => x);
        attrs = Array.from(new Set(attrs));

        // инициализация <option>
        initOptions(regions, regionFilter);
        initOptions(attrs, attrFilter);

        // генерация маппинга регион-достопримечательность
        DATA.data.reduce((acc, val) => {
            if (!acc[val.region.value]) {
                acc[val.region.value] = [];
            } else {
                acc[val.region.value].push(val.att_name.value);
            }
            return acc;
        }, regionAttrMap)

        for (let key in regionAttrMap) {
            regionAttrMap[key] = Array.from(new Set(regionAttrMap[key])).filter(x => x)
        }

        init = false;
    }
}

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

image

1 лайк