Создаем ToDoList приложение с Webix UI

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

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

А в этой статье я хочу поговорить о том, как создать небольшое ToDoList приложение на основе компонентов библиотеки Webix, которое поможет вам эффективно управлять ежедневными задачами и всегда будет у вас под рукой (в телефоне или лэптопе).

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

Webix ToDoList app

Обзор приложения

И так, нам нужно реализовать компактное приложение с дружественным интерфейсом, в котором пользователи смогут управлять своими ежедневными задачами, а именно:

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

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

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

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

Список активных задач

Active Tasks

Список выполненных задач

Completed Tasks

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

Подготавливаем почву

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

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

А теперь самое время заняться лейаутом приложения. И здесь стоит рассказать о том, что Webix использует декларативный подход в построении интерфейсов. Это значит, что каждый компонент представляет собой отдельный JS объект, в котором нужно указать все его параметры (основные настройки и обработчики событий) в виде пар ключ:значение.

Вкладывая такие объекты друг в друга, мы формируем лейаут приложения. А для инициализации лейаута, нам нужно передать его объект конструктору webix.ui() в качестве параметра.

Исходя из того, что хранить один большой JS объект не совсем удобно, мы разделим его на 2 логические части, о которых уже упоминали выше: тулбар и список задач. Каждую из них мы будем разрабатывать в отдельном файле и сохраним в соответствующую переменную. Эти файлы также нужно подключить в index.html.

<script type="text/javascript" src="js/toolbar.js"></script>
<script type="text/javascript" src="js/list.js"></script>

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

webix.ui({
  rows:[
    toolbar,
    list
  ]
});

А для полной уверенности в том, что код приложения начнет выполняться после полной загрузки HTML страницы, нам следует передать конструкцию лейаута методу webix.ready(function(){}).

webix.ready(function(){
  webix.ui({
    rows:[
      ...
    ]
  });
});

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

Создаем список задач

В первую очередь, давайте создадим список задач нашего приложения. И здесь не лишним будет упомянуть о том, что у библиотеки есть целая линейка виджетов, которые позволяют отображать данные в различном виде (таблицы, блоки, списки и т.д.). Речь идет о таких компонентах как datatable, treetable, dataview, list и другие. С полным перечнем можно ознакомиться здесь.

Исходя из того, что нам нужно отобразить задачи в виде обычного списка, давайте воспользуемся виджетом list. Каждый элемент нашего списка будет содержать название задачи и, в зависимости от ее статуса (активная или выполненная), иконки для управления этой задачей. Но об этом мы поговорим чуть позже.

Базовая конфигурация

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

const list = {
  view:"list",
  id:"todo_list",
  template:"#task#",
  url:"./data/list_data.json"
}

Сам компонент объявляется через выражение view: «list». Дальше мы задаем ему уникальный ID, с помощью которого будем обращаться к виджету для вызова его методов.

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

[
  { "id":1, "task":"English lessons", "status":true, "star":false },
  { "id":2, "task":"Yoga", "status":true, "star":true }, ...
]

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

data:[ { "id":1, "task":"Yoga", ... }, { ... }  ]

И так, компонент мы объявили и загрузили в него необходимые данные. Теперь нужно отобразить их в надлежащем виде. А за визуализацию данных отвечает свойство template, которому мы задали строковый шаблон «#task#». Этот шаблон будет отображать данные каждой задачи, которые хранятся в ее объекте под ключом task. В браузере список задач будет выглядеть так:

Basic List

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

Выше я уже упоминал о том, что в зависимости от статуса задачи (активная или выполненная), ее иконки будут отличаться. Помимо самого названия, для активных задач мы зададим:

  • иконку для перетаскивания задач в списке
  • иконку для добавления задач в избранные (и удаления из избранных)
  • иконку для завершения задач
  • иконку для удаления задач.

А для выполненных задач:

  • лейбл «Done»
  • иконку для возвращения задач обратно в активные.

Чтобы это реализовать, нам нужно создать специальную темплейт-функцию, которая будет возвращать необходимый шаблон задачи в зависимости от ее статуса. И здесь следует упомянуть о том, что статус каждой задачи хранится в объекте ее данных под ключом status.

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

const list_template = function(obj){
  if(obj.status){ // шаблон для активных задач
    return `
    <span class='drag_icon webix_icon mdi mdi-drag'></span>`+
        obj.task +`
    <span class='delete_icon webix_icon mdi mdi-delete'></span>
    <span class='complete_icon webix_icon mdi mdi-check-circle'></span>
    <span class='star_icon webix_icon mdi mdi-`+`
    (obj.star ? "star" : "star-outline")'
></span>`;
  }else{ // шаблон для завершенных задач
    return `
    <span class='done'>Done</span>`+
        obj.task +`
    <span class='undo_icon webix_icon mdi mdi-undo-variant'></span>`;
  }
}

Параметр obj — это объект данных конкретной задачи. Для начала, давайте проверим ее статус, который хранится в объекте. Если он равен true, то функция возвращает шаблон активной задачи, если же false — завершенной. Учитывайте, что при любых изменениях в данных задачи (включая смену статуса), она будет заново перерисована по этому шаблону.

Когда новый шаблон готов, давайте применим его к нашему приложению, а заодно установим фиксированную высоту задачи через свойство height и зададим ей стили через свойство css. А сделать все это можно в объекте свойства type. Конфигурация виджета теперь будет выглядеть следующим образом:

{
    view:"list",
    id:"todo_list",
    type:{
        height:45,
        css:"custom_item",
        template:list_template,
    },
    url:...
}

В браузере список задач будет выглядеть уже так:

Custom List

Список активных задач

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

Для этого мы создадим специальную функцию filterToDoList(status) которая будет принимать статус задачи и возвращать соответствующий результат: если статус равен true — активные задачи, если false — выполненные. Если же статуса нет вообще, мы будем сбрасывать фильтрацию списка в первоначальное состояние. Код функции будет выглядеть следующим образом:

function filterToDoList(status){
    const list = $$("todo_list");
    if (status === undefined){
        list.filter();
    } else {
        list.filter(function(obj){
            return obj.status === status;
        });
    }
}

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

Когда функция готова, нам следует вызвать ее в коллбэке свойства ready. Она выполнится, как только в виджет придут данные.

{
    view:"list",
    …,
    ready:function(){
        filterToDoList(true);
    }
}

Теперь, при первоначальной загрузке, пользователь увидит только список активных задач.

Редактирование задач

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

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

Всё API для редактирования Webix виджетов лежит в отдельном модуле webix.EditAbility. Чтобы получить редактируемый список, давайте создадим свой компонент на основе webix.ui.list с этим модулем.

А для того чтобы создать кастомный компонент, у библиотеки предусмотрен специальный конструктор protoUI. Ему следует передать название нового виджета (пусть это будет editlist), модуль редактирования и виджет, от которого будет наследоваться базовый функционал:

webix.protoUI({
    name:"editlist"
}, webix.EditAbility, webix.ui.list);

Теперь нам необходимо инициализировать новый виджет. А для этого, в конфигурации ранее описанного компонента, мы заменяем выражение view: «list» на view: «editlist» и добавляем следующие настройки редактирования:

    editable:true — включает редактирование для всех элементов списка
    editor:»text» — устанавливает редактор «text»
    editValue:»task» — определяет значение, которое будет редактироваться
    editaction:»dblclick» — открывает редактор при двойном клике по элементу (задаче).

Таким образом, если пользователь кликнет 2 раза по какой либо задаче, в браузере он увидит следующий результат:

Editing

Давайте немного усложним задачу и добавим валидацию, при которой редактируемое поле не должно оставаться пустым. А для этого мы воспользуемся свойством rules, в объекте которого пропишем правило webix.rules.isNotEmpty:

{
    view:"editlist",
    id:"todo_list",
    editable:true,
    …,
    rules:{
        task:webix.rules.isNotEmpty
    }
}

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

Validation

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

Чтобы это реализовать, нам следует установить специальный обработчик на событие onBeforeEditStart, которое будет срабатывать перед началом редактирования. В этом обработчике мы будем проверять и возвращать статус задачи. Если статус равен true (задача активная) — редактор запустится, а если false (задача выполненная) — нет.

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

{
    view:"editlist",
    …,
    on:{
      onBeforeEditStart:function(id, e){
        const obj = $$("todo_list").getItem(data);
        return obj.status;
      }
    }
}

С помощью метода getItem(), который принимает ID выбранного элемента списка, мы получаем объект задачи и возвращаем ее статус. Теперь пользователь может редактировать только активные задачи.

Перетаскивание задач

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

А за перетаскивание элементов списка отвечает свойство drag, которому можно задать одно из нескольких возможных значений. Например, для того чтобы пользователь мог перетаскивать задачи только в пределах списка задач, мы зададим свойству drag значение «inner»:

{
    view:"editlist",
    …,
    drag:"inner"
}

Более детальную информацию о перетаскивании элементов списка вы найдете в этой статье.

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

<span class='drag_icon webix_icon mdi mdi-drag'></span>

Давайте сделаем так, чтобы drag-n-drop срабатывал только на вышеуказанной иконке. А для этого, в объекте уже знакомого нам свойства on, мы установим обработчик на событие onBeforeDrag. Внутри обработчика нам нужно указать css класс иконки, на которой будет срабатывать событие. В нашем случае это drag_icon:

{
  view:"editlist",
  …,
  on:{
    onBeforeDrag:function(data, e){
        return (e.target||e.srcElement).className == "drag_icon";
    },
    …
  }
}

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

Избранные задачи

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

<span class='star_icon webix_icon mdi mdi-'+(obj.star ? "star" : "star-outline")></span>

Стоит учитывать, что в объекте каждой задачи хранится переменная star. Если ее значение равно true — задача находится в избранных и помечается иконкой с классом mdi-star (желтая звездочка), а если false — обычная задача с иконкой mdi-star-outline (простая звездочка). Для удобства, мы задали этим иконкам общий класс star_icon, который понадобится нам дальше.

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

Чтобы это реализовать, нам нужно установить обработчик на событие клика по иконке с классом star_icon. Это можно сделать в объекте свойства onClick, который предусмотрен специально для таких случаев:

onClick:{
  star_icon:function(e, id){
    const obj = this.getItem(id);
    this.updateItem(id, { star:!obj.star });
    if(obj.star){
      this.moveTop(id);
      this.showItem(id);
      this.select(id);
    }else{
      this.moveBottom(id);
    }
  }
}

В обработчике мы получаем объект данных задачи через метод списка getItem(), передав ее ID в качестве параметра. С помощью метода updateItem() нам нужно поменять значение star на противоположное, а затем проверить его.

Если обновленное значение star равно true — мы подымаем задачу на самый верх списка с помощью метода moveTop(), затем прокручиваем окно браузера к этому элементу через метод showItem(), а после этого выбираем элемент с помощью метода select(). В противном случае, мы отправляем задачу в самый конец списка используя метод moveBottom().

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

Для этого мы создадим специальную функцию sortToDoList(), которая будет сортировать задачи по их приоритету (star). Код функции будет таким:

function sortToDoList(){
    $$("todo_list").sort("#star#", "desc", "string");
}

Метод sort(), который мы используем внутри функции, принимает значение сортировки, ее порядок и тип сортируемых данных, а возвращает отсортированный список задач.

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

{
    view:"editlist",
    …,
    ready:function(){
        filterToDoList(true);
        sortToDoList();
    }
}

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

Завершение задач

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

<span class='complete_icon webix_icon mdi mdi-check-circle'></span>

Как и в предыдущем примере, нам нужно установить обработчик на событие клика по этой иконке, которой мы задали css класс complete_icon. А сделать это можно в объекте уже знакомого нам свойства onClick:

onClick:{
  star_icon: ...,
  complete_icon:function(e, id){
      this.updateItem(id, { status:false, star:false });
      this.moveTop(id);
      filterToDoList(true);
  }
}

Внутри обработчика мы делаем следующее:

  • сбрасываем статус задачи и ее приоритет с помощью метода updateItem()
  • поднимаем задачу в самый верх списка (выполненных задач) методом moveTop()
  • фильтруем активные задачи, чтобы скрыть только что завершенную, используя функцию filterToDoList(true).

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

Completing Tasks

Удаление задач

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

<span class='delete_icon webix_icon mdi mdi-delete'></span>

Здесь нам следует поступить также, как и с предыдущими иконками, а именно, установить обработчик на иконку с классом delete_icon:

onClick:{
  star_icon: ...,
  complete_icon: ...,
  delete_icon:function(e, id){
    this.remove(id);
    return false;
  }
}

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

Deleting Tasks

Список выполненных задач

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

List of Completed Tasks

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

<span class='undo_icon webix_icon mdi mdi-undo-variant'></span>

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

onClick:{
    star_icon: ...,
    complete_icon: ...,
    delete_icon: ...,
    undo_icon:function(e, id){
      this.updateItem(id, { status:true });
      this.moveBottom(id);
      filterToDoList(false);
    }
}

Внутри обработчика мы делаем следующее:

  • обновляем статус задачи с помощью метода updateItem()
  • перемещаем задачу в конец списка активных задач методом moveBottom()
  • фильтруем выполненные задачи, чтобы спрятать ту, которую мы сделали активной, используя функцию filterToDoList(false).

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

Returning Tasks

Создаем тулбар

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

Toolbar

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

  • 2 виджета label с иконкой и названием приложения
  • контрол button для создания новых задач
  • контрол search для поиска задач
  • контрол segmented для переключения между активными и завершенными задачами.

Теперь переходим к практике. В файле toolbar.js мы создаем тулбар и сохраняем его в переменную. Сам виджет объявляется с помощью выражения view: «toolbar». Нам следует задать ему встроенную стилизацию через свойство css и фиксированную высоту через свойство height.

Чтобы расположить содержимое виджета по горизонтали, нам необходимо поместить нужные элементы в массив его свойства elements.

const toolbar = {
    view:"toolbar",
    css:"webix_dark",
    height:45,
    elements:[...]
};

Лейблы с иконкой и названием мы объявляем с помощью выражения view: «label» и задаем им фиксированную ширину через свойство width. Название каждого лейбла нужно указать через свойство label.

elements:[
    { view:"label", width:30, label:"<span class='webix_icon mdi mdi-playlist-check'></span>" },
    { view:"label", width:130, label:"Webix ToDoList" }
]

Дальше у нас идет кнопка для создания новых задач. Для нее мы будем использовать контрол button, которому зададим уникальный ID через свойство id и название через свойство value. ID нам понадобится для того, чтобы получить доступ к кнопке и вызывать ее методы.

elements:[
    { view:"label",},,
    { view:"button", id:"create_button", value:"+ Create", width:120 }
]

После кнопки, нам следует разместить строку поиска задач, для которой мы используем контрол search. Его особенность заключается в том, что в отличии от обычного текстового поля (ui.text), в левой его части находится кликабельная иконка «поиск». А чтобы при вводе любых значений, вместо иконки «поиск» отображалась иконка «очистить», мы зададим виджету свойство clear в значении «hover». При клике по этой иконке, поле будет очищено.

elements:[
    { view:"label",},,
    { view:"button",},
    { view:"search", id:"search_input", clear:"hover", width:200 }
]

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

elements:[
    { view:"label",},,
    { view:"button",},
    { view:"search",},
    { view:"segmented", id:"segmented", width: 240,
      options: [
          { id:1, value:"Active" },
          { id:2, value:"Completed" }
      ]
    }
]

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

Создание новых задач

При клике по кнопке «+ Create», пользователь сможет добавить новую задачу, которая появится в конец списка активных задач. Чтобы это реализовать, нам нужно создать обработчик и установить его на событие клика по этой кнопке. А для обработки кликов, у кнопки предусмотрено специальное свойство click.

{ view:"button", id:"create_button", value:"+ Create", width:120,
  click:createNewTaskHandler }

Код обработчика будет выглядеть следующим образом:

function createNewTaskHandler(){
    const list = $$("todo_list");
    filterToDoList();
    const item = list.add({ task:"New Task", status:true, star:false });
    filterToDoList(true);
    list.showItem(item);
    list.select(item);
    list.edit(item);
}

Внутри функции мы делаем следующее:

  • сбрасываем фильтрацию списка, вызывая функцию filterToDoList() без аргумента
  • добавляем новую задачу с помощью метода add(), который принимает объект с нужными данными. Метод возвращает ID задачи, который мы сохраняем в переменную item и будем использовать в следующих методах
  • отфильтровываем активные задачи с помощью функции filterToDoList(status), передав ей статус активных задач в качестве аргумента
  • показываем новый элемент, передав его ID методу showItem(item)
  • выбираем новый элемент методом select(item)
  • открываем редактор задачи, используя метод edit(item).

Теперь, при клике по кнопке «+ Create», пользователь может добавить новую задачу в конец списка и сразу же ее редактировать.

Переключение между списками задач

Дальше у нас на очереди контролы для переключения между активными и выполненными задачами. Чтобы реализовать такое переключение, нам нужно установить обработчик на событие onChange контрола segmented. А сделать это можно в объекте уже знакомого свойства on:

{ view:"segmented", id:"segmented", width: 240,
  options: [
    { id:1, value:"Active" },
    { id:2, value:"Completed" }
  ],
  on:{ onChange:toggleHandler }
}

Код обработчика будет выглядеть следующим образом:

function toggleHandler(id){
  const button = $$("create_button");
  if(id == 2){
    filterToDoList(false);
    button.hide();
  }else{
    filterToDoList(true);
    button.show();
  }
}

Функция получает ID выбранного сегмента в качестве параметра. Если выбран сегмент «Completed», ID которого равен 2 — мы фильтруем список выполненных задач с помощью функции filterToDoList(status). Хочу напомнить, что функция принимает статус задач, которые нужно отфильтровать.

После фильтрации, нам нужно спрятать кнопку «+ Create», чтобы пользователь не смог создавать новые задачи находясь в списке выполненных задач. Для этого мы используем метод кнопки hide().

Если же пользователь выбрал сегмент «Active», мы фильтруем список активных задач и отображаем кнопку «+ Create» ее методом show().

Теперь пользователь может переключаться между активными и выполненным задачами с помощью соответствующих контролов «Active» и «Completed» на тулбаре.

Поиск задач

И нам осталось реализовать поиск задач через строку поиска, которую мы также разместили на тулбаре нашего приложения. Как вы уже догадались, нам опять нужно создать обработчик и установить его на событие ввода текста в объекте свойства on.

{ view:"search", id:"search_input", clear:"hover", width:200,
  on:{
    onTimedKeyPress:searchHandler
  }
}

Событие onTimedKeyPress, упомянутое выше, будет срабатывать через некоторое время после нажатия клавиши, если фокус-покус находится в поле контрола.

А код обработчика будет выглядеть следующим образом:

function searchHandler(){
    const search_value = $$("search_input").getValue().toLowerCase();
    const segmented_value = $$("segmented").getValue();
    const isMatched
    $$("todo_list").filter(function(obj){
        const is_matched = obj.task.toLowerCase().indexOf(search_value) !== -1;
        if(segmented_value == 2){
            return !obj.status && is_matched;
        }else{
            return obj.status && is_matched;
        }
    });
}

С помощью метода getValue() мы получаем текст из строки поиска и ID активного сегмента. Если выбран сегмент «Completed» (ID которого равен 2), мы фильтруем выполненные задачи, названия которых совпадают со значением строки поиска. В противном случае, нам нужно фильтровать активные задачи по такому же принципу.

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

Заключение

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

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