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

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

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



В двух словах


Webix — это JavaScript фреймворк, с помощью которого можно создавать десктопные и мобильные веб-приложения с отзывчивым дизайном. Фреймворк доступен под двумя лицензиями: GNU GPLv3 и коммерческой.

Особенности:

Легкость освоения. Документация довольно подробна, и понять, как все устроено, несложно. Для того, чтобы начать работу, не нужно быть JS-гуру или JS-ниндзя. Не нужно даже понимать, в чем разница между ними.
Интеграция с популярными фреймворками. Реализована интеграция с Backbone.js, AngularJS и jQuery. Последняя фича, например, позволяет создавать Webix-виджеты с использованием jQuery-синтаксиса.
Интеграция со сторонними виджетами. В этом пункте ограничимся списком: Mercury, Nicedit, Tinymce, CodeMirror, CKEditor, Raphael, D3, Sigma, JustGage, Google Maps, Nokia Maps, Yandex Maps, dhtmlxScheduler and dhtmlxGantt.
Размер — маленький, скорость — большая. В сжатом виде .js-файл весит всего 128 КБ, и при этом все работает довольно-таки быстро (по словам разработчиков так и вовсе «летает»).
Поддержка тачскрина. Созданные виджеты одинаково хорошо себя чувствуют как на десктопах, так и на смартфонах/планшетах.

От слов к делу


От списка особенностей, которые, думаю, мало кто читает от начала до конца, перейдем к практике. Но сначала предлагаю снова посетить главную страницу библиотеки и обратить внимание на интерактивную демку. Она состоит всего из десяти (10!) строк кода. Результат выглядит довольно симпатично. Если открыть эту демку в отдельном окне (вот ссылка на нее в онлайн-редакторе, если не хотите возиться) и поэкспериментировать с его размерами, можно убедиться в том, что виджет отображается корректно вне зависимости от предлагаемых обстоятельств. Может закрасться подозрение, что такое поведение не дается даром и нужно изрядно повозиться с таблицей стилей. Что ж, вот и проверим. Начать следует, как и положено, с начала.

Скачать и распаковать. Впрочем, можно обойтись и без этого


Для того чтобы начать использовать библиотеку, нужно сперва получить необходимые файлы. Для этого нужно отправиться на страницу загрузки. Выбираем понравившуюся версию (готов поспорить, в 99 случаях из 100 ей окажется Standard ) — и получаем заветный zip-файл. Внутри можно обнаружить файлы `license.txt`, `readme.txt` и `whatsnew.txt`, которые могут показаться любопытными для тех, кто любит изучать все досконально. Помимо этого, интересным может оказаться содержимое папки `samples`, в которой можно посмотреть примеры того, что полезного с помощью Webix можно смастерить.

Но больше всего на данный момент нас интересует содержимое папки `codebase`, а именно два файла, которые понадобятся для работы: `webix.js` и `webix.css`. Для того, чтобы можно было использовать Webix, нужно включить эти файлы в HTML-файл будущего проекта:

<!DOCTYPE html>
<html>
<head>
	<title>Online Audio PLayer</title>
	<link rel="stylesheet" href="codebase/webix.css" type="text/css"> 
	<script src="codebase/webix.js" type="text/javascript"></script>  
</head>
<body>
	<script type="text/javascript">
	</script>
</body>
</html>


Я также добавил теги <script></script>, внутрь которых и будет помещен код будущего приложения.

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

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


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

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


Теперь можно перейти непосредственно к работе с Webix.

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

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


Лэйауты


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

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

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

webix.ui({
    rows: [
        { template:"Row One"},
        { template:"Row Two"}
    ]
});


Вот как будет выглядеть результат:



В этом примере с помощью template:"Row One" был создан простой контейнер, в который можно поместить любой HTML-контент.

Можно создавать вложенные контейнеры:

webix.ui({
    rows: [
        { template:"Row One"},
        { cols:[
            { template:"Column One"}, 
            { template:"Column Two"} 
        ]}  
    ]
});


Результат:



Комбинируя вложенные строки и столбцы, можно добиться необходимого результата. По-умолчанию контейнеры заполняют все доступное пространство и, создавая четыре контейнера, мы получим четыре одинаковых прямоугольных области. Для того, чтобы задать нужные размеры элементов, можно использовать знакомые всем по CSS свойства width и height.

Таким образом, код для лэйаута нашего будущего плеера будет выглядеть вот так:

webix.ui({
	rows: [
		{type:"header", template:"Online Audio Player"},
		{cols: [
			{rows: [
				{template:"Tree"},
				{view:"resizer"},
				{template:"Album Art", width: 250, height: 250}
			]},
			{view:"resizer"},
			{rows: [
				{template:"Playlist"},
				{template:"Controls", height: 60}
			]}	
		 ]
		}
	]
});


Помимо уже знакомых колонок и столбцов, я также добавил кое-что новое. type:"header" превращает элемент в заголовок. Также были добавлены несколько элементов resizer, которые, как и следует из названия, нужны для изменения размеров контейнера. Ресайзер, как и все прочие UI-компоненты, создаeтся с помощью свойства view, которое позволяет создавать списки, кнопки, формы и т.д.

Вот как выглядит макет будущего приложения на данном этапе:



Теперь у нас есть области нужных размеров и возможность изменить эти размеры на свой вкус.

Страница размечена, настало время вдохнуть жизнь в элементы нашего приложения.

Посадить дерево… Знакомимся с Tree Widget


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

recordsData = [  
{id:"1", value:"Oceansize", data: [
    {id:"1.1", value:"Everyone Into Position"},
    ]},
{id:"2", value:"Little People", data: [
    {id:"2.1", value:"Mickey Mouse Operation"},
    ]},
];


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

<script src="data.js"></script>


Во-вторых, вместо template:"Tree" в фигурные скобки нужно вставить код, который создаст дерево:

view:"tree", data:"recordsData", select: true


Эта строка создает дерево на основе данных из массива recordsData из файла данных. select: true дает возможность выбирать один из элементов дерева, отключенную по-умолчанию.

Вот как это выглядит на данный момент:



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


Теперь давайте посмотрим, как работают таблицы данных. Для того, чтобы проверить их работу, нужно добавить в файл data.js два новых массива. gridColumns содержит названия столбцов, которые будут отображаться в нашем списке, а oceanData содержит информацию о треках для первого альбома первого исполнителя: название и продолжительность каждой песни.

gridColumns = [
    {
        dataIndex: "title",
        header: "Title"
    },
    {
        dataIndex: "duration",
        header: "Duration"
    }
];

oceanData = [
    {id: "1", title: "01. The Charm Offensive", duration:"7:19"},
    {id: "2", title: "02. Heaven Alive", duration:"6:20"},
    {id: "3", title: "03. A Homage to Shame", duration:"5:52"},
    {id: "4", title: "04. Meredith", duration:"5:26"},
    {id: "5", title: "05. Music for a Nurse", duration:"8:16"},
    {id: "6", title: "06. New Pin", duration:"5:11"},
    {id: "7", title: "07. No Tomorrow", duration:"7:10"},
    {id: "8", title: "08. Mine Host", duration:"4:10"},
    {id: "9", title: "09. You Can’t Keep a Bad Man Down", duration:"7:36"},
    {id: "10", title: "10. Ornament. The Last Wrongs", duration:"9:21"}
];


Для того, чтобы отобразить эти данные в виде сортируемой таблицы, нужно заменить template:"Playlist" на:

view:"datatable", autoConfig:true, data:oceanData


Работает этот код так: view:"datatable" создает таблицу данных, autoConfig: true помогает сконфигурировать таблицу и создать столбцы на основе данных из массива gridColumns. В качестве расходного материала для таблицы используется массив oceanData.

Вот как выглядит приложение с таблицей:



Прошу заметить, что данные в таблице можно сортировать но названиям или по длительности песни.

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

Операция «Кооперация». Заставляем дерево и список работать сообща


Для этого понадобится проделать некоторую подготовительную работу. Я постараюсь ее минимизировать, чтобы поскорее насладиться результатом.

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

recordsData = [  
{id:"1", value:"Oceansize", data: [
    {id:"oceanData", value:"Everyone Into Position"},
    ]},
{id:"2", value:"Little People", data: [
    {id:"peopleData", value:"Mickey Mouse Operation"},
    ]},
];

peopleData = [
    {id: "1", title: "01. Basique", duration: "3:38"},
    {id: "2", title: "02. Moon", duration: "3:47"},
    <......>
];


Значение id для альбомов из массива recordsData совпадает с именем массива информации для соответствующего альбома. Это важно, потому что так будет быстрее.

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

var selected = oceanData;
webix.ui({
	/*.....*/
	{view:"tree", id:"myTree", data:"recordsData", select: oceanData, on: {
		onSelectChange: function(){
			selected = $$("myTree").getSelectedId();
			if (isNaN(selected)) {
				$$("myList").clearAll();
				$$("myList").define("data", selected);
				$$("myList").refresh();
			}
		}
		}
	},
	/*...*/
	{view:"datatable", id:"myList", autoConfig:true, data:selected}


Итак, что мы здесь имеем? Переменная selected по-умолчанию хранит значение первого альбома из дерева, данные которого и отображаются в таблице сразу после загрузки приложения. В код дерева и таблицы были добавлены атрибуты id со значениями myTree и myList соответственно. Они нужны для того, чтобы получить доступ к содержимому этих элементов. Свойство `select: oceanData` определяет `id` элемента списка, выбранного по-умолчанию. Далее используется обработчик событий. Код

	on: {
	onSelectChange: function(){
	}


выполняется в тот момент, когда пользователь выбирает новый элемент в дереве. Когда это происходит, выполняется код, помещенный в тело функции. В нашем случае происходит следующее: строка selected = $$("myTree").getSelectedId(); присваивает переменной id выбранного в данный момент элемента дерева. Для папки это будет цифра, соответствующая ее номеру в иерархии, а для альбома — имя массива, содержащего информацию о нем. Поскольку выводить на экран информацию о папке в наши планы не входит, сперва нужно проверить, не является ли возвращенное значение цифрой: if (isNaN(selected)). Если это не цифра (а нам только этого и нужно), то список обновляется с помощью этого кода:

$$("myList").clearAll();
$$("myList").define("data", selected);
$$("myList").refresh();


Сначала список очищается, затем инициализируется измененным значением selected с помощью метода define(), который принимает два значения: свойство, которое нужно изменить, и новое значение для этого свойства. Затем содержимое таблицы обновляется.

И, собственно, результат:



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

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

Для визионеров


Ну что ж, придется попотеть еще немного, чтобы увидеть обложки. Для этого понадобится новая переменная, которая будет содержать путь к обложке по-умолчанию:

var coverPath = "imgs/oceanData.jpg";


Поскольку в этом случае, как и в предыдущем, вид приложения меняется в соответствии с изменениями в дереве, код необходимый для смены обложек также нужно добавлять в код дерева. Сразу после $$("myList").refresh(); добавим такие строки:

coverPath = "imgs/" + selected + ".jpg"
$$("myCover").define("data", {src: coverPath});


Названия файлов обложек совпадают со значениями id соответствующих альбомов, а значит, можно снова использовать значение переменной selected по аналогии с предыдущим шагом. Затем нужно изменить состояние контейнера `myCover`, в котором отображается обложка. Для этого был использован уже знакомый метод define().

Осталось заставить соответствующий контейнер отображать картинку. Для этого строку template:"Album Art", width: 250, height: 250 нужно заменить на:

	width: 250, height: 250, id:"myCover", data: {src:coverPath},
	template: function (obj) {
	    return '<img src="'+obj.src+'"/>'
  	}


Этот пример не так очевиден на первый взгляд. Давайте разбираться. width и height были установлены заранее, ничего нового тут нет; id нужен для доступа к содержимому контейнера — тоже без сюрпризов; а вот дальше начинается самое интересное: свойство data определяет содержимое контейнера. Значение src соответствует пути к файлу изображения, и именно его нужно изменить, чтобы изменилась обложку. Последующая функция нужна для генерации HTML-кода, и ее без лишней нужды лучше не трогать.

Теперь проверим, как работает отображение обложек. Внешний вид по-умолчанию:



Внешний вид после выбора нового элемента дерева:



Обложки меняются, как и задумано. Осталось добавить кнопки управления.

Берем приложение под контроль


Самая ответственная часть нашего примера. Добавление кнопок. Поскольку наш плеер минималистичен донельзя, обойтись можно тремя кнопками: Previous, Play / Pause и Next.

Поскольку некоторый опыт работы с Webix уже имеется, этот последний штрих не будет таким уж сложным. Для добавления кнопок нужно заменить template:"Controls", height: 60 на:

cols: [ {view:"button", value:"< Previous"}, {view:"button", value:"Play / Pause"}, {view:"button", value:"Next >"}]


Здесь создаются три столбца, в каждый из которых добавляется по кнопочке.

Вот результат:



Итоги


Ну что тут можно сказать. Самое сложное — создание интерфейса с некоторой интерактивностью — позади. Пустяки, вроде возможности воспроизведения файлов, каждый сможет реализовать сам. Для того, чтобы создать отзывчивый(responsive) интерфейс, мне не пришлось возиться с css-кодом. Разработчики уже повозились с ним за меня, а мне осталось только использовать свойства `width` и `height`, чтобы придать элементам нужный размер.

На этом, пожалуй, всё… Или..? Ах, да! Чуть не забыл о скинах. Прикрутить их к готовому проекту несложно, поэтому много времени это не займет.

Рестайлинг на лету


В каталоге skins можно обнаружить вот такой вот список доступных вариантов:

  • aircompact.css
  • clouds.css
  • glamour.css
  • metro.css
  • touch.css
  • air.css
  • compact.css
  • flat.css
  • light.css
  • terrace.css
  • web.css


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

<link rel="stylesheet" type="text/css" href="codebase/skins/touch.css">


можно изменить вид приложения на такой:



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

Ну а на этом, пожалуй, действительно все. Надеюсь, статья помогла вам освоить основы работы с Webix. Продолжить погружение вам поможет доступная и подробная документация с множеством примеров.

Адьо!

Комментарии (22)


  1. uSide
    24.04.2015 12:45
    +9

    Это как ExtJS для бедных?


    1. stannislav Автор
      24.04.2015 12:51
      +10

      Это как ExtJs, без необходимости получать докторскую степень, чтобы разобраться в коде.

      А если серьезно, то похоже.
      Только в три раза меньше по размеру, существенно быстрее и без необходимости создавать тонны кода на каждый чих.


      1. JetHedgehog
        24.04.2015 15:09
        -2

        От себя добавлю: и с великолепными компонентами типа PivotChart / PivotTable. Качественного аналога PivotChart пока что ни в ExtJS не видел, ни где либо еще…


      1. Aclz
        24.04.2015 15:46
        +2

        В ExtJS не нужно писать тонны кода, код там очень лаконичен. Но даётся со скрипом, да.


        1. stannislav Автор
          24.04.2015 16:03
          -2

          Загрузка данных в Webix

          webix.ui({
             view:"datatable", url:"'/projectLink/gridData.php"
          })
          


          Загрузка данных в extJs

          var store = new Ext.data.Store({ 
              fields:['mainid','Country'],
              proxy: {
                  type: 'ajax',
                  url : '/projectLink/gridData.php',
                  reader: {
                      type: 'json',
                      root: 'rates',
                      totalProperty : "count"
                  }
              },
              autoLoad : true
          });;
          
          var grid = Ext.create('Ext.grid.Panel',{
                store:store
          });
          


          Чтобы загрузить данные надо задать Store, в нем Proxy в нем Reader или просто указать откуда брать данные. И это я еще не создавал Модель как описано в оф. документации ExtJs.

          Понятно что это дает гибкость в настройке, но зачем весь этот лишний код когда мне надо просто загрузить данные.


          1. Aclz
            24.04.2015 18:43
            +6

            1. Ваш пример для Ext можно сократить примерно вдвое, избавившись от установки значений, и так являющихся дефолтными, плюс, в случае, если у нас есть доступ к gridData.php. В этом случае не обязательно описывать все эти root и totalProperty (в противном случае, если у нас есть левые ноды, то и ваше webix.ui работать не будет, т.к. не разберется из какой ноды брать данные).

            2. Не могу похвастать владением webix, но есть подозрение, что вы приводите несколько упрощенный пример. Лаконичность фреймворков нужно сравнивать при равных возможностях конкретной подсистемы.

            В гриде может потребоваться редактирование данных (REST или вообще произвольный API) или форматирование данных, требующее типизации, или сервер у вас возвращает не json, а, скажем XML, или же json, но сервер не ваш и формат, соответственно, не 100% готов быть загружен в UI «как есть» (скажем, есть лишние поля или лишние корневые теги, или какие-то колонки должны быть скрытыми, типа id записи базы, но присутствовать в store для связи/записи в базу), или грид может быть автоматически декларативно сджойнен с подчиненным гридом.

            Если ничего этого в функционале фреймворка нет — то понятно, что и методы будут предельно лаконичны, без лишних параметров, и грид можно грузить готовым json в одну строчку. Но зато шаг влево, шаг в право — и упираемся в непреодолимые грабли (или необходимость написания самописных модулей и прочих костылей уже на чистом JS).


            1. stannislav Автор
              25.04.2015 00:34

              Мои знания extJs более теоретические, чем практические. Тем не менее, код выше взят из документации обоих фреймворков и вроде как представляет стандартный путь решения одной и тойже задачи — загурзки данных. В случае с Webix там дается простое решение, которое может быть усложнено. В случае extJs там дается сложное решение которое можно, как оказывается, упростить. Мне больше аппелирует первый подход, от простого к сложному.

              В гриде может потребоваться редактирование данных (REST


              webix.ui({
                 view:"datatable",
                 url:"'rest->/projectLink/gridData.php",
                 save:true
              })
              


              или сервер у вас возвращает не json, а, скажем XML


              webix.ui({
                 view:"datatable",
                 url:"'/projectLink/gridData.php",
                 datatype:"xml"
              })
              


              То есть для выполнения часто используемых операций нет необходимости вводить дополнительные абстракции. Для сложных случаев в Webix тоже можно создавать Proxy объекты и Reader-ы ( тут они называются DataDriver ). Это таже разница в подходах о которой я говорил выше. Простые вещи делаются просто, а когда нужно что то нестандартное, тогда можно копнуть глубже.


  1. ParaPilot
    24.04.2015 13:11
    +1

    Пример не работает


    1. stannislav Автор
      24.04.2015 13:17

      Музыку не играет, это да.
      А сам интерфейс вроде как рисуется, данные выбираются и меняются.


      1. ingumsky
        24.04.2015 13:48
        +1

        Наверное, предыдущий комментатор имел в виду, что по нажатию на кнопки Previous и Next нет перехода на следующий элемент в таблице.


        1. stannislav Автор
          24.04.2015 14:29

          Да, и это тоже не работает.
          Демка содержит только те же шаги что описаны в статье.

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


  1. Zeliret
    24.04.2015 14:21
    +1

    У меня белая страница вообще в последнем Хроме. :(


    1. stannislav Автор
      24.04.2015 14:28
      +1

      Странно, у меня вроде бы все работает ( и в стабильном хроме и в бетте ), возможно где то я все же накосячил


      1. zaynetro
        24.04.2015 15:57

        Страница загружается с https, а скрипты с незащищенного http, поэтому хром и блокирует их.


        1. stannislav Автор
          24.04.2015 16:07

          Спасибо.
          Поправил демку, теперь должна работать и по https


  1. yroman
    24.04.2015 21:43
    +1

    Потыкался по сайту — так и не нашёл список поддерживаемых браузеров.


    1. stannislav Автор
      24.04.2015 23:57

      Можно найти в глубинах документации
      docs.webix.com/desktop__compatibility.html

      Работает везде начиная с IE8


  1. BjornValor
    24.04.2015 22:30

    Над названием не мудрствовали лукаво :) Вообще — «чтонить»-кс наверное самый популярный способ придумывать названия технологиям со времён *nix

    P.S.: А вообще молодцы, за такие вещи беретесь это все равно вклад в индустрию.


  1. lifecom
    25.04.2015 02:51

    … создавать десктопные и мобильные веб-приложения с отзывчивым дизайном

    почему же демо получилось не отзывчивым?


    1. stannislav Автор
      25.04.2015 17:13

      По умолчанию интерфейсы не являются польность отзывчивыми ( responsive ). Размеры подстраиваются под экран, но сам интерфейс не перестраивается. Насколько я представляю, можно задать правила какие элементы куда должны перемещаться на мелком экране, но с этим я толком сам не разобрался.

      docs.webix.com/desktop__responsive_layout.html


  1. yurash
    25.04.2015 09:48
    +1

    простые примеры — это хорошо, но на них и официальной документации хватает. Хотелось бы увидеть что-нибудь сложнее, в частности разбор «Demo of Webix MVC app» github.com/webix-hub/webix-adminapp-demo


  1. Frost47rus
    25.04.2015 12:04

    Неделю уже экспериментирую с этим фреймворком. Сделал вывод, что он идеально подходит для веб-морд админок (CRUD).
    Радует ещё и то, что практически вся работа с webix сведена к js коду, без html и css.
    Определённо буду использовать этот отличный инструмент.