Webix Datatable. От простой таблицы к сложному приложению

Эта статья будет интересна для тех, кто привык решать сложные задачи простыми методами. Работа с большими данными, на первый взгляд, может показаться сложной задачей. Но если вы владеете специальными инструментами, то организация и отображение больших наборов данных покажется вам не более чем забавным развлечением. Сегодня мы поговорим об одном из самых неординарных инструментов для работы с данными, который предоставляет нам команда Webix. Речь пойдет о таком простом и одновременно сложном виджете библиотеки Webix UI как DataTable. Давайте разбираться в чем его сила.

Библиотека Webix и виджет DataTable

Webix UI — это современная и производительная JavaScript библиотека, которая позволяет создавать дружественный интерфейс на основе собственных ui компонентов. Диапазон ее возможностей представлен виджетами различной сложности, начиная обычной кнопкой и заканчивая комплексными виджетами. Помимо самих компонентов, библиотека предоставляет множество дополнительных инструментов для работы с ними. Здесь стоит упомянуть механизм обработки событий, методы для работы с данными, взаимодействие с сервером, темы для стилизации и многое другое. Обо всем этом и не только, вы можете узнать в документации

Виджет DataTable - это один из самых функциональных компонентов библиотеки Webix. С его помощью вы можете отображать данные в виде таблиц и очень гибко их настраивать. Этот мощный и одновременно простой в использовании инструмент со стильным дизайном поддерживает различные форматы данных (XML, JSON, CSV, JSArray, HTML tables) и довольно быстро работает с большими объемами информации. Секрет его скорости заключается в так называемом "ленивом подходе отрисовке данных". Это не значит, что ему лень отрисовывать данные. Хотя, без сомнений, крупица правды в этом есть. Суть же подхода заключается в том, что даже если вы загрузите в таблицу 1 000 000 рядов, выджет отрисует только выдимые в окне браузера элементы. Стоит также сказать, что среди своих конкурентов виджет удерживает лидирующее место по скорости отрисовки.

Помимо прочего, DataTable обладает широким арсеналом API для фильтрации, сортировки, пагинации, копирования, изменения размеров, стилизации и многих других полезных фишек. Все это делает DataTable незаменимым помощником при работе с большими данными.

Можно много рассуждать об удобстве работы с таблицами Webix и их обширных возможностях, но я предлагаю оставить патетику ораторам и попробовать разобраться во всем на практике. Давайте создадим небольшое приложение, которое будет отображать таблицу данных об аренде автомобилей. На наглядном примере гораздо проще увидеть все преимущества работы с этим мощным инструментом. Успешное применение этого виджета также описано в статье "Создаем Booking приложение с Webix UI". 

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

Базовые приготовления

Для того чтобы использовать возможности библиотеки Webix в нашем приложении, необходимо подключить ее в главном файле index.html. Здесь стоит упомянуть о том, что существует 2 версии библиотеки: базовая и Pro-версия. Базовая версия бесплатная и предоставляет ограниченный набор возможностей, по сравнению с Pro-версией. Мы же воспользуемся тестовой лицензией расширенной Pro-версии, чтобы по максимуму реализовать возможности виджета DataTable. Необходимые файлы доступны через CDN по следующим ссылкам: 

<script type="text/javascript" src="http://cdn.webix.com/site/webix.js"></script>
<link rel="stylesheet" type="text/css" href="http://cdn.webix.com/site/webix.css">

Нам остается только включить их в файл index.html нашего приложения. Теперь он будет так:

<!DOCTYPE html>
<html>
  <head>
    <title>Webix Booking</title>
    <meta charset="utf-8">
    <!--Webix sources -->
    <script type="text/javascript" src="http://cdn.webix.com/site/webix.js"></script>
    <link rel="stylesheet" type="text/css" href="http://cdn.webix.com/site/webix.css">		
  </head>
  <body>
    <script type="text/javascript">
      //...
    </script>
  </body>
</html>

Внутри кода мы добавим теги <script>...</script>, где и будем собирать наше приложение.

Инициализация

Все основные действия будут разворачиваться внутри конструктора webix.ui(). Нам же нужно удостовериться в том, что код начнет выполняться после полной загрузки HTML страницы. Для этого необходимо обернуть конструктор в webix.ready(function(){}). Выглядит это следующим образом:

webix.ready(function(){
  webix.ui({
    /*код приложения*/
  });
});

Мы создали базовый index.html файл и подключили необходимые инструменты. Теперь самое время перейти непосредственно к настройке самого компонента DataTable. 

Сила в простоте

Если вы минималист и не горите желанием разбираться во всех тонкостях настройки виджета, то для вас предусмотрен самый “ленивый” набор конфигураций, который позволяет отобразить нужную информацию в виде таблицы. В файле datatable.js нужно создать конструктор виджета и прописать три следующих строчки кода:

const datatable = {
  view:"datatable",
  autoConfig:true,
  url:"./data/data.json"
}

Сам компонент DataTable объявляется с помощью выражения view:"datatable". Через свойство url мы задаем путь, по которому виджет загружает данные. Стоит уточнить, что по умолчанию виджет ожидает получить данные в формате JSON. Если данные приходят в другом формате (xml, jsarray или csv), нужно указать его через свойство datatype. В случае, когда данные находятся на клиенте в виде массива, их можно передать компоненту через свойство data или метод parse().

Особое внимание стоит уделить свойству autoConfig, которое предназначено для максимально ленивых программистов. С его помощью данные автоматически распределяются по соответствующим столбцам и рядам таблицы. Удобнее только вообще ничего не делать. 

Теперь давайте отобразим компонент в браузере и посмотрим, что же у нас получилось. Для удобства, мы сохраним конструктор виджета в переменную datatable, которую будем использовать для сборки в файле index.html.

С помощью следующего кода мы подключаем файл с компонентом DataTable в файле index.html:

<!--App sources -->
<script src="js/datatable.js" type="text/javascript" charset="utf-8"></script>

В конструкторе приложения мы указываем переменную, которая хранит настройки виджета:

<script type="text/javascript">
 webix.ready(function(){
  webix.ui( datatable );
 });
</script>

В браузере мы увидим следующий результат:

Aвтонастройка
Aвтонастройка

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

Тонкости настроек таблицы

Если автонастройка кажется вам слишком уж тривиальным вариантом, тогда давайте усложним задачу и зададим индивидуальные настройки для каждого столбца в отдельности. Сделать это можно в массиве свойства columns:[ ]. Для каждого столбца нужно задать объект с соответствующими настройками. Стоить учитывать то, что порядок отображения столбцов в таблице напрямую зависит от порядка объектов с настройками в массиве.

В первую очередь, для каждого столбца нужно задать уникальный id. Он должен соответствовать ключу, под которым данные хранятся в объекте (хранилище). Затем можно указать название для “шапки” столбца с помощью свойства header. 

После этого можно задать ширину каждого столбца. Для этого предусмотрены такие свойства как width, minWidth, maxWidth и fillspace. Если мы хотим, чтобы ширина таблицы подстраивалась под ширину контейнера или под доступное ей место, нужно использовать свойство fillspace по крайней мере для одного столбца. Теперь настройки столбцов будут выглядеть так:

{
  view:"datatable",
  columns:[
    { id:"rank", header:"Rank", width:45 },
    //...
    { id:"vin_code", header:"VIN", minWidth:50, width:180, maxWidth:300 },
    //...
    { id:"address", header:"Address", minWidth:200, fillspace:true },
    //...
  ],
  url:"./data/data.json"
}

В браузере мы получим следующий результат:

Индивидуальные настройки столбцов
Индивидуальные настройки столбцов

В этом примере видно, что столбцы размещены в установленном нами порядке и с заданной шириной, а их названия соответствуют значениям свойства header.

Помимо индивидуальных настроек ширины для каждого столбца, вы можете задать глобальные настройки размеров для всех столбцов в конструкторе компонента. Для этого предусмотрено свойство autowidth, которое подстраивает ширину виджета к суммарной ширине всех столбцов таблицы, а также свойство columnWidth, которое задает фиксированную ширину для всех столбцов. По умолчанию стандартная ширина столбца  равна 100px.

Для полноты картины давайте предоставим пользователям возможность изменять размеры столбцов. Для этого необходимо задать свойству resizeColumn значение true, а также установить границы для рядов, столбцов и их хедеров с помощью свойства css:"webix_data_border webix_header_border".

Так как у таблицы есть много столбцов, необходимо предусмотреть горизонтальную и вертикальную прокрутку. Сделать это можно с помощью свойства scroll, которое изначально установлено в значении false. Нам же нужно задать ему значение “xy”.

Теперь мы можем изменять размер столбцов, перетаскивая их правую границу в нужном направлении, а также скролить таблицу по горизонтали и вертикали.

Настройка содержимого ячеек

Работа с шаблонами

По умолчанию, ячейки таблицы заполняются данными, ключ которых задан в качестве id в настройках столбца. Но виджет позволяет нам управлять их отображением. С помощью свойства template мы можем задать необходимый шаблон, по которому данные будут отображаться в ячейке. Значение можно указать как в виде строки, так и в виде функции. Чтобы использовать в строковом шаблоне входящие данные, их ключ нужно указать как #data_key#. У нас есть несколько столбцов, для которых необходимо задать шаблоны. 

Начнем мы пожалуй с первого столбца, в ячейках которого будут размещаться иконки в виде звездочек. У вас может возникнуть резонный вопрос, зачем нам такой столбец со звездочками? А мы сделаем так, чтобы наш потенциальный пользователь смог отмечать понравившиеся ему варианты при клике по звездочке. Давайте это реализуем с помощью темплейт-функции. Код будет выглядеть следующим так:

{
  view:"datatable",
  id:"car_rental_table",
  //...
  columns:[
    { id:"stared", header:"",
     template:function(obj){
       return `<span class='webix_icon star mdi mdi-"+(obj.star ? "star" : "star-outline") + "'></span>`;
     }, ...,
    }, 
    //...
  ]
}

Свойству template мы присваиваем функцию, которая возвращает элемент span с определенными классами. Классы “star” и “star-outline” мы будем менять динамически при клике по иконке. Давайте создадим функцию, которая будет менять классы для иконок этого столбца:

function selectStar(id){
  const table = $$("car_rental_table");
  const item = table.getItem(id);
  const star = item.star?0:1;
  item.star = star;
}

В качестве аргумента функция принимает id выбранного ряда. Через метод $$("car_rental_table") мы получаем доступ к виджету по его id. С помощью метода таблицы getItem(), который принимает id элемента в качестве параметра, мы получаем объект данных ряда. Затем проверяем наличие ключа star и присваиваем ему значение 0 (если он существует) либо 1 (если его нет).

Чтобы переключение начало работать, необходимо установит эту функцию в качестве обработчика на событие клика по иконке со звездочкой. Для того чтобы установить обработчик на любой элемент таблицы с определенным css классом, у таблицы предусмотрено специальное свойство onClick:

//...
url:"./data/data.json",
onClick:{
  "star":(e,id) => selectStar(id)
},
//...

Теперь мы можем отмечать нужные нам ряды при клике по звездочке в первом столбце таблицы. В браузере это выглядит следующим образом:

Шаблон для столбца со звездочками
Шаблон для столбца со звездочками

На очереди у нас столбец с названием  “Available”. В его ячейках хранятся значения true и false, которые обозначают доступность автомобиля в текущий момент времени. Давайте зададим шаблон, который будет менять входящее значения ячейки на соответствующий текст “Yes” или “No”.

Для этого мы создадим специальную функцию, которая и будет менять нужные нам значения. Выглядит она так:

function customCheckbox(obj, common, value){
  if(value){
    return "<span class='webix_table_checkbox checked'> YES </span>";
  }else{
    return "<span class='webix_table_checkbox notchecked'> NO </span>";
  }
}

Теперь нужно установить эту функцию в качестве шаблона для столбца “Available”:

columns:[
  //...
  { id:"active", header:"Available", template:customCheckbox, ...,},
]

В браузере результат будет следующим:

Шаблон для столбца "Available"
Шаблон для столбца "Available"

Выглядит уже неплохо. Так как в первых трех столбцах у нас находятся данные, которые постоянно должны быть на виду, давайте их зафиксируем в левой части таблицы. Для этого необходимо добавить свойство leftSplit в конструктор виджета и задать ему количество желаемых столбцов (в нашем случае 3). Теперь эти столбцы будут отображаться поверх других элементов при вертикальном скролле. 

А сейчас давайте займемся столбцом под названием “Color”. Его значения отображаются в виде HEX кодов, которые обозначают цвет автомобиля. Для обычного пользователя такие значения ничего не говорят. Давайте добавим визуальное представление цвета для каждой ячейки. И сделаем мы это также с помощью шаблона. Код будет выглядеть так:

columns:[
  //...
  { id:"color", header:"Color", template:`<span style="background-color:#color#; 
border-radius:4px; padding-right:10px;">&nbsp</span> #color#`},
  //...
]

Здесь мы используем строковый шаблон, в котором задаем фон неразрывного пробела (&nbsp) с помощью входящего HEX кода.

В браузере результат будет следующим:

Шаблон для столбца "Color"
Шаблон для столбца "Color"

Работа с коллекциями 

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

Для примера, давайте рассмотрим столбец с названием "Сar make", в котором должны отображаться марки автомобилей. Данные для его ячеек хранятся в виде чисел от 1 до 24 под ключем "car_make":

//data.json
[
  { "id":1, "rank":1, "car_make":22, ..., "country":1, "company":1, ..., },
  { "id":2, "rank":2, "car_make":10, ..., "country":2, "company":3, ..., },
  { "id":3, "rank":3, "car_make":16, ..., "country":1, "company":2, ..., },
  //...
]

Чтобы заменить числа в столбце на соответствующие названия автопроизводителей, нам нужно получить доступ к коллекции, в которой хранятся эти названия. Выглядит коллекция следующим образом:

//car_make.json
[
  { "id":22, "value":"Toyota" }, ...,
  { "id":10, "value":"GMC" }, ...,
  { "id":16, "value":"Mazda" }, ...,
  //...
]

В настройки столбца необходимо добавить свойство collection и присвоить ему путь к нужному объекту (коллекции):

columns:[
  //...
  { id:"car_make", header:"Car make", collection:"./data/car_make.json", ...,},
  //...
]

Вот таким образом, вместо числовых значений, в ячейках столбца “Car make” будут отображаться названия автопроизводителей. По такому же принципу мы заменяем значения для столбцов “Company”, “Country” и “Card”.

В браузере результат будет следующим:

Коллекции для столбцов
Коллекции для столбцов

Работа с форматами данных

При работе с данными, не редко возникает потребность изменить формат их отображения. На этот случай, у таблицы имеется специальное свойство format. С его помощью мы можем задать желаемый формат отображения чисел и дат. Для этого предусмотрены специальные методы. Давайте зададим формат отображения для столбцов с датами и ценой заказа:

columns:[
  //...
  { id:"date", header:"Date", format:webix.i18n.longDateFormatStr, ..., },
  { id:"price", header:"Price", format:webix.i18n.priceFormat, ..., },
  //...
]

Данные о датах приходят в виде строк “05/26/2021”. Нам же нужно получить дату в формате “26 May 2021”. Метод webix.i18n.longDateFormatStr, который мы применили в настройках столбца “Date”, должен получать объект Date и возвращать строку в нужном формате. Но сейчас он получает только строку типа “05/26/2021”, поэтому результат может быть неожиданным. Давайте изменим входящие данные и преобразуем строки в соответствующие Date объекты.

Для этого у таблицы предусмотрено свойство scheme. В объекте этого свойства мы меняем строковое значение даты на соответствующий объект с помощью метода webix.i18n.dateFormatDate. Код будет выглядеть следующим образом:

{
  view:"datatable",
  //...
  scheme:{
    $init:function(obj){
      obj.date = webix.i18n.dateFormatDate(obj.date)
    }
  },
  columns:[...]
}

С форматированием даты мы разобрались. Теперь давайте посмотрим как изменить цену в столбце "Price". А здесь все еще проще. Метод webix.i18n.priceFormat получает число (например 199) и возвращает строку со знаком доллара в начале: “$199”. Вот и вся хитрость.

В браузере результат будет следующим:

Форматирование даты и цены
Форматирование даты и цены

Узнать больше о возможностях форматирования данных библиотеки Webix можно в этой статье. 

Сортировка данных

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

В виджете DataTable эта задача решается при помощи всего лишь одного свойства sort. Нам нужно только включить его в настройки необходимого столбца и задать правильный режим. Так как мы будем сортировать столбцы с датами, числами и строками, нам понадобятся следующие типы сортировки:

  • "int" - сравнивает числовые значения

  • "date" - сравнивает даты

  • "string" - сравнивает строковые значения в том виде, в котором они загружаются

  • "text"- сравнивает текст элемента, который отображается в ячейке (включая темплейт)

    columns:[
      { id:"car_model", header:"Model", width:120, ..., sort:"string", }, ...,
      { id:"car_year", header:"Year", width:85, ..., sort:"int" }, ...,
    	{ id:"country", header:"Country", width:140, ..., sort:"text" }, ...,
    	{ id:"date", header:"Date", width:150, ..., sort:"date" }, ...,
    ]

Теперь данные будут сортироваться при клике по хедеру определенного столбца. Более того, мы можем установить режим, который позволяет сортировать данные по нескольким критериям одновременно. Для этого нужно задать свойству sort значение "multi"  в конструкторе виджета. Чтобы отсортировать данные по нескольким условиям, нужно нажать клавишу Ctrl/Command и кликнуть по хедерам нескольких столбцов.

Если вы хотите применить другое поведение сортировки (отличное от встроенного), вы можете задать нужную логику в отдельной функции и присвоить ее атрибуту sort.

Если же нужно отсортировать данные таблицы при клике по какой либо внешней кнопке, вы можете воспользоваться методом таблицы sort(). Подробнее о сортировке данных вы можете узнать в этой статье.

Фильтрация данных

Сложно представить себе полноценную таблицу без возможности фильтровать в ней данные. И такая возможность предусмотрена в таблице Webix. С помощью свойства content, мы можем добавить один из нескольких встроенных фильтров или же задать собственные условия фильтрации. Это позволит нам фильтровать данные на клиенте или сервере по одному или нескольким критериям.

В зависимости от типа данных в столбцах, мы будем применять для них разные типы фильтров. Давайте добавим фильтр selectFilter с выпадающим списком опций в хедер столбца с названием “Company”. Для этого же столбца мы ранее указали свойство collection, которое заменяет числовые значения соответствующими названиями компаний. Фильтр сам подтянет данные этой коллекции и сформирует из них список опций. В настройках столбца это выглядит так:

columns:[
  //...
  {
    id:"company", 
    header:["Company",{content:"selectFilter"}], 
    collection:"./data/company.json", ...,
  }, ...,
]

В браузере результат будет следующим:

Фильтр selectFilter
Фильтр selectFilter

Для столбца с названием “Car make” мы добавим фильтр textFilter. Он представляет собой обычное поле для ввода данных. Фильтр будет сравнивать введенные значения с данными столбца. Хочу напомнить, что данные приходят сюда в виде чисел, которые преобразуются в соответствующие названия моделей авто. Дело в том, что фильтр будет сравнивать введенные значения именно с числами, а это нас не совсем устраивает. Давайте изменим поведение фильтра по умолчанию и сделаем так, чтобы введенные значения сравнивались с данными из коллекции. Для этого мы добавим к фильтру специальную функцию для сравнения:

columns:[
  //...
  { id:"car_make", header:["Car make", {
    content:"textFilter", placeholder:"Type car make",
    compare:function(item, value, data){ 
      const colValue = cars_make_data.getItem(item).value;
      const toFilter = colValue.toLowerCase();
      value = value.toString().toLowerCase();
      return toFilter.indexOf(value) !== -1;
    } }], collection:cars_make_data, ...,
  },
  //...
]

Такой же фильтр мы добавим следующему столбцу с названием “Model”. Его данные приходят в виде строк, так что нам не придется устанавливать дополнительные условия:

columns:[
  //...
  { id:"car_model", header:["Model", {content:"textFilter", placeholder:"Type model"}, ...,],
  //...
]

В браузере результат будет следующим:

Фильтр textFilter
Фильтр textFilter

Теперь давайте перейдем к столбцу с названием “Year”, в ячейках которого отображается год производства автомобилей. Здесь мы добавим такой фильтр как excelFilter. Особенность его заключается в том, что мы можем вводить данные в поле ввода, задавать дополнительные условия и отмечать нежелательные элементы в специальных чекбоксах. Настройки столбца будут выглядеть так:

columns:[
  //...
  { id:"car_year", header:[{text:"Year", content:"excelFilter", mode:"number"}], ...,},
  //...
]

С помощью свойства mode мы указываем, что будем сравнивать числа. Вместо поля ввода, возле хедера появится специальная иконка, при клике по которой откроется окно для ввода условий. В браузере результат будет следующим:

Фильтр excelFilter
Фильтр excelFilter

Среди наших столбцов есть и такой, в котором отображаются доступные даты аренды автомобилей. Для работы с датами у виджета есть специальный фильтр datepickerFilter. Он позволяет выбрать необходимую дату в компактном всплывающем календаре. Настройки столбца будут выглядеть так:

columns:[
  //...
  { id:"date", header:["Date", {content:"datepickerFilter"}], ..., },
  //...
]

В браузере результат будет следующим:

Фильтр datepickerFilter
Фильтр datepickerFilter

Вот таким образом мы можем добавлять нужные нам фильтры для поиска данных в столбцах. С полным списком фильтров вы можете ознакомиться в этой статье документации.

Редактирование данных

Функционал виджета позволяет редактировать данные непосредственно в ячейках таблицы. Чтобы активировать эту опцию, необходимо задать свойству editable значение true в конструкторе таблицы. Также можно определить действие, по которому будет открываться редактор ячейки. По умолчанию, редактор открывается при клике по ячейке. Можно также определить открытие по двойному клику (“dblclick”) или указать собственное действие (“custom”). 

Когда режим редактирования активирован, в настройках столбца нужно задать необходимый тип редактора. В зависимости от типа данных в столбце, мы будем применять соответствующий редактор.

А начнем мы из самого часто используемого редактора под названием text. Он превращает ячейку в обычное поле для ввода, куда можно ввести нужные нам данные. Давайте добавим его для нескольких столбцов нашей таблицы. Делается это очень просто: в настройках столбца нужно всего лишь указать свойство editor и присвоить ему соответствующий тип редактора. Настройки столбцов будут выглядеть так:

{
  view:"datatable",
  //...
  editable:true,
  editaction:"dblclick",
  columns:[
    { id:"rank", header:"Rank", editor:"text", ..., },
    { id:"car_model", header:"Model", editor:"text", ..., },
    { id:"manager", header:"Manager", editor:"text", ..., },
    //...
  ],
  //...
}

При двойном клике по любой из ячеек этих столбцов, в браузере мы увидим следующий результат:

Редактор "text"
Редактор "text"

Если в ячейке будет находиться большой текст, то редактировать его в маленьком поле будет не очень удобно. Для таких случаев предусмотрен редактор popup. Он позволяет редактировать данные в специальном всплывающем окне. По умолчанию ширина и высота окна равны 250px и 50px соответственно. Давайте добавим этот редактор в настройки столбца “Address”:

columns:[
  { id:"address", header:"Address", editor:"popup", ...,},
  //...
],//...

При двойном клике по любой ячейке столбца “Address”, в браузере мы увидим следующий результат:

Редактор "popup"
Редактор "popup"

Теперь перейдем к столбцу c названием “Available”. Как вы помните, для него мы задали шаблон, который превращает значения true и false в соответствующие строки YES и NO. Давайте сделаем так, чтобы пользователь смог переключаться между этими значениями. Для этого мы используем специальный редактор inline-checkbox. Он позволяет менять значения в ячейке при клике по ней. Но для работы этого редактора также необходимо задать свойству checkboxRefresh значение true. Это свойство обновляет данные, полученные из checkbox-редакторов в таблице. Настройки столбца будут выглядеть так:

{
  //...
  checkboxRefresh:true
  columns:[
    //...
    { id:"active", header:"Available", editor:"inline-checkbox", template:customCheckbox, ..., },
  //...
  ],
  //...
}

При клике по любой ячейке этого столбца, его значение изменится на противоположное.

А на очереди у нас столбцы, данные для которых подтягиваются из коллекций. Для них лучше всего подойдет редактор combo. Он позволяет выбирать нужную опцию из выпадающего списка. Список формируется автоматически исходя из данных коллекции столбца. Настройки столбцов будут выглядеть так:

columns:[
  { id:"company", header:"Company", editor:"combo", 
   collection:"./data/company.json", ..., },
  { id:"car_make", header:"Car make", editor:"combo", 
   collection:cars_make_data, ..., },
  { id:"country", header:"Country", editor:"combo",
   collection:"./data/country.json", ..., },
  //...
],
//...

При двойном клике по ячейкам этих столбцов, в браузере мы увидим следующий результат:

Редактор "combo"
Редактор "combo"

Особого внимания заслуживает столбец под названием “Color”. Хочу напомнить, что входящие данные представляют собой HEX коды различных цветов. У таблицы Webix есть специальный редактор, который позволяет выбрать необходимый цвет в специальном всплывающем окне, а его код отобразится в ячейке столбца. Речь идет о таком редакторе как color. Настройки столбца будут выглядеть так:

columns:[
  { id:"color", header:"Color", editor:"color", template:..., },
  //...
], 
//...

При двойном клике по любой ячейке столбца “Color”, в браузере мы увидим следующий результат:

Редактор "color"
Редактор "color"

Теперь нам осталось разобраться с редактированием данных в столбце “Date”. Для работы с датами у таблицы предусмотрен специальный редактор date. Как и в случае с фильтром, возле ячейки появится компактный календарь, где вы сможете выбрать нужную дату, которая сразу отобразится в редактируемой ячейке. Настройки столбца будут выглядеть так:

columns:[
  { 
    id:"date", header:"Date", editor:"date", 
    format:webix.i18n.longDateFormatStr, ..., 
  },
  //...
], 
//...

При двойном клике по любой ячейке столбца “Date”, в браузере мы увидим следующий результат:

Редактор "date"
Редактор "date"

Более подробно о редактировании таблицы можно узнать в соответствующей статье документации. А мы двигаемся дальше.

Валидация

После редактирования данных, они должны сохраняться на сервере. Здесь хорошим тоном будет проверить измененную информацию перед тем как ее сохранить. Как это сделать спросите вы? Да все донельзя просто. У библиотеки Webix есть специальные правила, которые можно применить для каждого редактируемого поля. В зависимости от типа данных, мы будем применять разные правила. 

У нас есть несколько столбцов с числовыми значениями, для которых мы установим правило webix.rules.isNumber. Таблица будет проверять, является ли значение в текстовом поле числом.

Также у нас есть столбец, который содержит адреса электронной почты. Его значения мы будем проверять правилом webix.rules.isEmai.

Для столбца с ценами необходимо задать собственные условия проверки. Давайте сделаем так, чтобы пользователь мог ввести только данные в диапазоне от 20 до 500 долларов. Для этого мы создадим специальную функцию: 

function(obj){ return (obj>20 && obj<500) }

Все остальные столбцы мы будем проверять правилом webix.rules.isNotEmpty. Это значит, что в любом случае они должны быть заполнены. 

Чтобы применить все эти правила, у таблицы есть специальное свойство rules. Внутри объекта этого свойства необходимо указать id нужных столбцов и присвоить им соответствующие правила. Выглядит это так:

column:[...],
rules:{
  rank:webix.rules.isNumber,
  company:webix.rules.isNotEmpty,
  email:webix.rules.isEmail,
  price:function(obj){ return(obj>20 && obj<500) },
  // правила для других столбцов	
}

По умолчанию, валидация начнется после закрытия редактора. Если введенные значения не соответствуют правилам, то целый ряд подсветится красным, а в правом верхнем углу невалидной ячейки появится красная метка:

Валидация
Валидация

Хедеры и футеры 

Если вы работаете с большими данными, которые разделяются на множество столбцов, часто возникает необходимость объединить их названия в определенные категории. Такой подход помогает структурировать таблицу и упрощает поиск нужной информации. Виджет DataTable позволяет объединять хедеры с помощью свойств colspan и rowspan, которые немного похожи на настройки обычной HTML таблицы. Для примера, давайте посмотрим как объединить столбцы Price, Card и IBAN в категорию Payment information. Для этого нужно немного изменить свойство header вышеуказанных столбцов:

column:[
  //...
  { id:"price", header:[{text:"Payment information", colspan:3}, "Price"], ..., },
  { id:"credit_card", header:["","Card"], ..., },
  { id:"iban", header:["","IBAN"], ..., },
  //...
]

В браузере мы получим следующий результат:

Объединяем хедеры
Объединяем хедеры

Если хедеры подключены по умолчанию, то футеры нужно активировать отдельно. Для этого необходимо задать свойству footer значение true в конструкторе виджета. Давайте определим название футера для первого столбца и объединим его с футером второго столбца при помощи свойства colspan. А в футере столбца “Available”, где хранятся данные о доступных автомобилях, мы будем подсчитывать и отображать активные варианты. Настройки столбцов будут выглядеть так:

column:[
  //...
  { id:"stared", header:[...], ..., footer:{ text:"Available:", colspan:2 } },
  //...
  { id:"active", header:[...], ..., footer:{content:"summColumn"}, ..., },
	//...
]

Элемент, заданный как {content:"summColumn"} , будет подсчитывать все значения равные true и отобразит их количество в футере. Все изменения в ячейках столбца “Available” незамедлительно отобразятся в его футере. В браузере мы получим следующий результат:

Футеры
Футеры

Управление видимостью столбцов

Если ваша таблица состоит из множества столбцов, то было бы очень удобно спрятать ненужные в данный момент варианты. Разработчики Webix позаботились о решении этого вопроса и реализовали такую опцию как headerMenu. С ее помощью мы можем управлять видимостью столбцов через специальный интерфейс. По желанию, его можно настроить по своему усмотрению.

Давайте добавим иконку headerMenu в качестве хедера для первых двух столбцов и настроим всплывающее окно меню. Код будет выглядеть так:

//...
headermenu:{
  width:210,
  data:[ 
    { id:"car_year", value:"Year" },
    { id:"color", value:"Color" },
    { id:"vin_code", value:"VIN" },
    { id:"phone_number", value:"Phone" },
    //...
  ]
},
column:[
  { id:"stared", header:[{ content:"headerMenu", colspan:2, ...,}], ..., },
  { id:"rank", header:["",""], ..., },
  //...
]

В объекте свойства headermenu мы задаем ширину всплывающего окна и массив столбцов, которые можно будет прятать при клике по специальной иконке в виде глаза. 

В браузере мы увидим такой результат:

Опция headermenu
Опция headermenu

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

Пагинация для таблицы

Если количество рядов таблицы достаточно большое, то использовать очень длинную вертикальную прокрутку не совсем удобно. Для решения этого вопроса можно добавить пагинацию. И как вы наверное уже догадались, виджет DataTable предоставляет нам такую возможность.

Для начала, давайте создадим модуль пагинации в файле pager.js. Его код будет выглядеть так:

//pager.js
const pager = {
  view:"pager",
  id:"pager",
  size:20,
  group:5,
  template:`{common.first()} {common.prev()} 
{common.pages()} {common.next()} {common.last()}`
};

С помощью свойств size и group мы устанавливаем количество элементов на странице (20) и число видимых кнопок для пагинатора (5).  Вот, в принципе, и все настройки. Также можно задать свойство template, которое определяет кнопки для переключения страниц (помимо кнопок с цифрами).

Теперь давайте подключим модуль с компонентом в файл index.html и добавим переменную с пагинатором в конструктор приложения:

//index.html
<!--App sources -->
<script src="js/datatable.js" type="text/javascript" charset="utf-8"></script>
<script src="js/pager.js" type="text/javascript" charset="utf-8"></script>
//...
<script type="text/javascript">
  webix.ready(function(){
    webix.ui({
      rows:[
        datatable,
        {cols:[
          {},pager,{}
        ]}
      ]
    });
  });
</script>

Ну и в завершение, давайте свяжем пагинатор с таблицей, чтобы при переключении кнопок, данные таблицы менялись в соответствии с настройками (по 20 рядов на странице). Для этого нужно задать свойству pager значение id созданного нами пагинатора в конструкторе таблицы. Вот такими нехитрыми манипуляциями реализуется пагинация для таблицы в библиотеке Webix. В браузере мы увидим следующий результат:

Пагинация
Пагинация

Операции с рядами таблицы

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

Стоит отметить, что в библиотеке Webix есть несколько способов добавлять иконки. Можно использовать иконки из встроенного шрифта (<span class='webix_icon wxi-drag'></span>), или специальные встроенные элементы (common.trashIcon()). 

Чтобы это реализовать, нужно перейти к массиву свойства columns и добавить следующие настройки:

column:[
  //...
  { 
    header:[{text:"<span class='webix_icon wxi-plus-circle'></span>", colspan:2}], 
    width:50, template:"<span class='webix_icon wxi-drag'></span>" 
  },
  { header:["",""], width:50, template:"{common.trashIcon()}" }
]

Теперь в конце таблицы появятся 2 дополнительных столбца, в каждом ряду которых будут находиться иконки для удаления и перетаскивания. Так как столбцов у нас достаточно много, давайте, для удобства, зафиксируем эти 2 столбца с иконками в конце таблицы, чтобы они постоянно были на виду и не прокручивались. Для этого, в конструкторе виджета необходимо задать свойству rightSplit значение 2. В браузере мы увидим следующий результат:

Иконки для операций с рядами
Иконки для операций с рядами

Иконки у нас готовы. Теперь давайте установим обработчики на событие клика по этим иконкам. Чтобы поймать событие клика по любому элементу таблицы с определенным css классом, необходимо воспользоваться свойством onClick. В объекте этого свойства нужно указать класс иконки и присвоить ему соответствующий обработчик. В нашем случае, мы ловим клик по иконкам с классами wxi-plus-circle и wxi-trash:

onClick:{
  "wxi-plus-circle":() => addNewElement(), //добавляет элемент
  "wxi-trash":(e,id) => removeElement(id), //удаляет элемент
  //...,
}

Теперь давайте создадим эти обработчики. Функция для добавления новых данных будет выглядеть так:

function addNewElement(){
  const table = $$("car_rental_table"); //получаем доступ к таблице
  //добавляем данные
  const id_new_elem = table.add({"active":0,"color":"#1c1919","date":new Date()}); 
  table.showItem(id_new_elem); //показываем новый элемент в таблице
}

С помощью метода таблицы add() мы можем добавить в нее новые данные. Этот метод возвращает id новой записи, который мы передаем другому методу таблицы showItem(), чтобы показать (проскролить) этот элемент в таблице.

Функция для удаления записи будет выглядеть так:

function removeElement(id){
  $$("car_rental_table").remove(id);
}

Метод таблицы remove() получает id выбранного элемента в качестве параметра и удаляет его из таблицы.

Нам осталось решить вопрос с перетаскиванием рядов. Эта опция активируется с помощью свойства drag. В конструкторе таблицы ему нужно задать значение “order”. После этого, мы можем перетаскивать любой ряд (вверх или вниз) и оставлять его в нужном месте.

Сейчас перетаскивать элемент можно за любую его часть. Давайте ограничим зону перетаскивания на специально созданной иконке с классом wxi-drag .

Для этого мы воспользуемся свойством on, в объекте которого и установим обработчик на событие onBeforeDrag:

on:{
  onBeforeDrag:function(data, e){ 
    return (e.target||e.srcElement).className == "webix_icon wxi-drag";
  }
}

В браузере мы увидим следующий результат:

Перетаскивание рядов
Перетаскивание рядов

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

Тулбар с дополнительными опциями

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

В файле toolbar.js мы создаем компонент toolbar, внутри которого определяем кнопки Reset filters, Add column и Export to Excel. Выглядит это так:

const toolbar = {
  view:"toolbar",
  css:"webix_dark",
  height:50,
  //...
  cols:[	
    //...	
    { view:"button", label:"Reset filters", click:resetFilters },
    { view:"button", label:"Add column", click:addColumn },
    { view:"button", label:"Export to Excel", click:exportToExcel }
  ]
};

С помощью свойства click мы устанавливаем обработчики на событие клика по кнопкам. Давайте создадим эти обработчик. А начнем мы из функции, которая будет сбрасывать фильтрацию данных таблицы и сами фильтры. Код будет выглядеть так:

function resetFilters(){
  const table = $$("car_rental_table");
  table.filter(); 
  table.showItem(table.getFirstId()); 
  table.setState({filter:{}}); 
}

Метод filter(), вызванный для таблицы без параметров, отображает данные в первоначальном порядке. С помощью метода таблицы setState() мы очищаем значения полей фильтров.

Следующей на очереди у нас функция, которая будет добавлять новые столбцы. Код будет выглядеть так:

function addColumn(){
  const table = $$("car_rental_table");
  table.config.columns.splice(3,0,{
    id:"c"+webix.uid(),
    header:`<span class="webix_icon wxi-close-circle" 
webix_tooltip="Delete column"></span>Extra column`,
    editor:"text",
    width:120
  });
  table.refreshColumns();
}

С помощью свойства таблицы config.columns мы получаем массив с настройками столбцов и добавляем туда объект с настройками нового столбца в 4 позицию. Для этого используем js метод splice(). Когда данные изменены, нужно обновить представление столбцов с помощью метода таблицы refreshColumns().

И у нас осталась только функция, которая будет экспортировать данные таблицы в формате Excel. Код будет выглядеть так:

function exportToExcel(){
  webix.toExcel("car_rental_table", {
    filename:"Car Rental Table",
    filterHTML:true,
    styles:true
  });
}

Внутри функции мы используем метод webix.toExcel(), которому передаем id таблицы и объект с необходимыми настройками. Вот и вся хитрость.

Когда все уже готово, нужно включить файл toolbar.js в файл index.html и добавить переменную toolbar в конструктор приложения:

webix.ui({
  rows:[
    toolbar,
    datatable,
    {cols:[
    	{},pager,{}
    ]}
  ]
});

В браузере наше приложение будет выглядеть следующим образом:

Тулбар с кнопками
Тулбар с кнопками

Теперь мы можем сбрасывать фильтрацию данных, добавлять новые столбцы, а также экспортировать данные в формате Excel.

Остался еще один нюанс. При создании нового столбца, в его хедер мы добавляем иконку с классом wxi-close-circle. Нам нужно установить обработчик на событие клика по этой иконке, который будет удалять столбец. Сделать это можно в объекте свойства onClick:

onClick:{
  //...
  "wxi-close-circle":(e,id) => deleteColumn(id)
}

Теперь давайте создадим этот обработчик:

function deleteColumn(id){
  const table = $$("car_rental_table");
  table.clearSelection();
  table.editStop();
  table.refreshColumns(table.config.columns.filter(i=>i.id !== id.column));
}

Через свойство config.columns мы получаем массив настроек столбцов, отфильтровываем из него ненужный элемент и передаем обновленный массив методу таблицы refreshColumns()

В браузере мы увидим следующий результат:

Добавляем и удаляем новый столбец
Добавляем и удаляем новый столбец

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

Заключение

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

В этой статье мы детально разобрали, как создать приложение на основе виджета DataTable библиотеки Webix UI, а также научились настраивать таблицу с помощью большого разнообразия свойств и методов. Как вы могли убедиться, все настройки интуитивно понятны и просты в применении. На самом деле, это только малая часть всего, что может предложить нам таблица Webix. Для более детального ознакомления можно перейти к документации, где вы сможете найти подробное описание всех возможностей компонента с наглядными примерами.

68 0