
Я — начинающий front-end разработчик. Сейчас я учусь и стажируюсь в одной минской IT компании. Изучение основ web-ui проходит на примере JS библиотеки Webix. Я хочу поделиться своим скромным опытом и сохранить его в виде небольшого учебного пособия по этой интересной UI библиотеке.
ПЯТАЯ ЗАДАЧА
В прошлой статье, Работа с данными, на основе функций CRUD я писал о стандартных задачах по управлению данными приложения. На этом этапе я попытаюсь сделать приложение более дружественным для пользователя и реализую возможность фильтровать, сортировать и группировать данные. В статье я рассмотрю следующие задачи:
- фильтрация и сортировка данных таблицы;
- фильтрация и сортировка через API;
- группировка данных древовидной таблицы;
- синхронизация компонентов;
В документации можно ознакомиться с использованными в статье виджетами List, Treetable, Table.
Исходники находятся по ссылке.
С готовым приложением можно ознакомиться тут.
Фильтрация и сортировка данных таблицы
Начнем с таблиц — их я чаще всего использовал для работы с большим количеством данных. Таблицы в библиотеке Webix имеют ряд встроенных фильтров, которые устанавливаются прямо в header виджетов Table и TreeTable. В виджете Table я использую два варианта: простой текстовый фильтр (textFilter) и фильтр с набором опций в выпадающем списке (selectFilter). Таблица позволяет добавить по одному фильтру для каждой колонки. Сделаю это для двух: title и year. Header в них вместо строки задам массивом — чтобы уместить заголовок и фильтр. Второй элемент массива, это объект со свойством content и именем фильтра.
Код виджета Table находится в файле table.js и отрисован во вкладке “Dashboard”.
При вводе символов в
"textFilter" данные будут отфильтрованы совпадением по подстроке. При выборе опции в "selectFilter" — по выбранному значению.columns:[ { id:"rank", header:"", width:50, css:"rank"}, { id:"title", header:["Film title", { content:"textFilter"}], fillspace:true }, { id:"year", header:["Released", {content:"selectFilter" }], width:100 }, { id:"votes", header:"Votes", width:100 }, { id:"rating", header:"Rating", width:100 }, { header:"", template:"<span class='webix_icon wxi-close'></span>", width:35} ]
Результат фильтрации по подстроке “star”:

Результат фильтрации элементов по выбранному значению “1991”:

Сортировка. Как и в случае с фильтрами, сортировку так же легко сделать доступной для пользователя. Для этого достаточно дополнить конфигурацию колонок свойством sort. Есть несколько готовых типов сортировки: по числовым значениям, по дате и по строке. Столбцам year, votes и rating я установлю настройку sort: “int” для сортировки по числовым значениям. Для столбца title значение будет “string”.
columns:[ { id:"rank", header:"", width:50, css:"rank"}, { id:"title", header:["Film title", { content:"textFilter"}], fillspace:true, sort:"string"}, { id:"year", header:["Released", {content:"selectFilter" }], width:100, sort:"int"}, { id:"votes", header:"Votes", width:100, sort:"int"}, { id:"rating", header:"Rating", width:100, sort:"int"}, { header:"", template:"<span class='webix_icon wxi-close'></span>", width:35} ]
По клику на header колонки данные будут отсортированы в соответствии с их типом. Результат сортировки по рейтингу:

Сортировка и фильтрация через API
Готовые решения для фильтрации и сортировке элементов имеют только таблицы. Но в целом все виджеты поддерживают эти возможности через соответствующие методы API — filter и sort. Фильтрацию и сортировку при помощи API я продемонстрирую у виджета List.
Код виджета List находится в файле users_module.js и отрисован во вкладке “Users”.
Фильтрация. Во вкладке Users, после кнопки “Add new person”, я установлю виджет Text, который использую в качестве фильтра для имен из списка.
cols:[ { view:"button", id:"btn_add_person", value:"Add new person", width:150, css:"webix_primary", click:addPerson }, { view:"text", id:"list_input" }, ]
Теперь открою файл script.js и добавлю логику отвечающую за фильтрацию элементов.
$$("list_input").attachEvent("onTimedKeyPress",function(){ var value = this.getValue().toLowerCase(); $$("user_list").filter(function(obj){ return obj.name.toLowerCase().indexOf(value) !== -1; }) });
Фильтрация элементов происходит по такому принципу:
- при помощи метода attachEvent я добавляю обработчик на событие onTimedKeyPress;
- событие onTimedKeyPress вызывается вводом символов в текстовое поле, но с короткой задержкой, чтобы не задействовать фильтр при каждом нажатии клавиш;
- далее я получаю введенный текст и методом filter запускаю фильтрацию — поиск совпадений в виджете List.
Результат фильтрации через API:

Сортировка. Сортировка элементов виджета List будет происходить по клику кнопок
“Sort asc” и “Sort desc”. Чтобы создать кнопки во вкладке Users, после текстового поля я добавлю два виджета Button с обработчиком событий click.
cols:[ { view:"button", id:"btn_add_person", value:"Add new person", width:150, css:"webix_primary", click:addPerson }, { view:"text", id:"list_input" }, { view:"button", id:"btn_asc", width:150, value:"Sort asc", css:"webix_primary", click:()=>{ $$("user_list").sort("#name#","asc") }}, { view:"button", id:"btn_desc", width:150, value:"Sort desc", css:"webix_primary", click:()=>{ $$("user_list").sort("#name#","desc") }}, ]
Внутри обработчика click, метод sort принимает для параметра: имя поля, по которому сортируем данные, и направление сортировки
“asc” (ascending) — по возрастанию, и “desc” (descending) — по убыванию. По умолчанию данные считаются строками и сортируются соответствующим образом.Результат (имена в листе отсортированы по алфавиту):

Группировка данных древовидной таблицы
Рассмотрим ситуацию когда данные надо сгруппировать по произвольным параметрам.
Изучать группировку я буду на примере виджета TreeTable.
Код виджета TreeTable находится в файле products_module.js и отрисован во вкладке “Poducts”.
В статье: Модули, диаграммы, древовидные таблицы и Работа с данными. CRUD, в древовидной таблице я использовал иерархические данные. Для решения этой задачи я их изменил, чтобы получить линейный массив. Я избавился от иерархии и перенес из нее в каждую запись поле
“category”.[ {"id": "1.1", "title": "Standard Ticket", "price": 21, "category":"Cinema", "rank":1.1}, {"id": "2.1", "title": "Cola", "price": 10, "category":"Cafe", "rank":2.1}, {"id": "3.1", "title": "Flowers", "price": 10, "category":"Other", "rank":3.1} ]
Сгруппировать данные в таблице можно двумя способами:
Параметры у этих методов одинаковые и мне нужно сгруппировать данные один раз, как только они пришли, поэтому я использую первый вариант.
В конфигурации виджета TreeTable добавлю свойство scheme. Это свойство определяет по какой схеме будут обработаны данные в разных ситуациях. Среди обработчиков в sheme есть метод $group, который мне нужен для группировки данных.
const products = { editable:true, view:"treetable", scrollX:false, columns:[ { id:"rank", header:"", width:50 }, { id:"title", header:"Title", fillspace:true, template:"{common.treetable()} #title#"}, { id:"price", header:"Price", width:200, editor:"text" } ], select:"row", url:"data/products.js", scheme:{ $group:{ by:"category", map:{ title:["category"] } }, $sort:{ by:"value", dir:"asc" } } }
Внутри обработчика $group использовано два параметра:
- обязательный параметр by, по которому группируются данные. Здесь — одно из полей (“category”);
- объект map — здесь опишем поля данных для создаваемых групп. Группировка разбивает исходные данные по указанным параметрам и создает для них «родительские записи». Через map мы можем добавить в эти записи новые поля. Чтобы данные в таблице отображались корректно, добавлю поле “title”. Передам в него значение параметра, по которому происходит группировка.
Дополнительно я установил функцию $sort, чтобы отсортировать сгруппированные данные в алфавитном порядке.
Результат группировки:

Синхронизация компонентов
В задаче использованы виджеты Chart и List, код которых в файле users_module.js и отрисованы во вкладке “Users”
Виджеты Chart и List используют одни и те же данные — массив JSON. Эти компоненты можно связать так, чтобы все изменения данных в одном из них (источнике) транслировались в другой. Для этого используется метод sync.
Метод sync позволяет копировать данные из одного компонента и передавать их другому. При этом, изменения в основном компоненте, такие как добавление, удаление, и пр., сразу отражаются в другом.
Для начала, в виджете Chart — диаграмма — я удалю ссылку на данные.
{ view:"chart", id:"chart", type:"bar", value:"#age#", label:"#age#", xAxis:{ template:"#name#", title:"Age" } }
Теперь, в файле script.js методом sync я синхронизирую виджет Chart с виджетом List.
$$("chart").sync($$("user_list"));
В функции addPerson добавление оставляем только для виджета List.
let addPerson = () => { let obj = { name:"Some name", age:Math.floor(Math.random() * 80) + 10, country:"Some country" } $$("user_list").add(obj); };
Теперь при добавлении и удалении записей из списка, изменения будут происходить и в диаграмме. Сортировка и фильтрация в виджете List теперь затронет и данные в Chart.

Обобщение
На практических примерах я показал как можно улучшить работу пользователя. Помимо стандартной сортировки и фильтрации при помощи API была освоена встроенная возможность сделать это в виджетах Table и TreeTable одной настройкой. Был продемонстрирован способ группировки данных в таблице, а пример синхронизации расширил возможность усовершенствовать работу виджетов, использующих один и тот же источник данных.
С готовым приложением можно ознакомиться тут.
