Pull to refresh

Простой компонент на TypeScript для отображения дерева

Reading time 3 min
Views 10K
В процессе работы над небольшим web-проектом мне потребовался компонент для отображения дерева элементов на странице. Компонент должен позволять развернуть/свернуть узлы дерева, обработать клик по элементу, добавить к дереву новые узлы, в общем предоставлять самые основные функции «treeview».

Условия использования компонента при этом несколько отличаются от «общепринятых» в лучшую сторону — в качестве среды исполнения будут применяться современные (IE9+) браузеры (web-проект предполагается использовать в рамках полностью контролируемой внутренней сети). Еще одним моментом является платформа, на базе которой разрабатывается серверная часть. Это ASP.NET MVC, а значит желательно, чтобы компонент поставлялся в том числе в виде NuGet-пакета, дружественного к типовой структуре каталогов ASP.NET MVC-приложения.

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


На момент написания этой статьи, в сети Интернет можно найти шесть наиболее популярных компонентов для отображения дерева элементов.
  1. jsTree. Судя по прошлогоднему обсуждению jsTree на хабрахабр, некоторая часть разработчиков отдает предпочтение Dynatree.
  2. jqTree. Производит наиболее приятное впечатление. Возможно из-за неплохого дизайна и подробной документации.
  3. jQuery TreeView plugin. Автор сообщает о том, что проект больше активно не развивается и рекомендует использовать jqTree.
  4. Dynatree.
  5. dhtmlxTree.
  6. dTree.

Некоторые выводы.
  • Для всех характерны накладные расходы по поддержке старых клиентов.
  • Первые четыре требуют для своей работы jquery. Пятый использует свой фреймворк. Только последний является самодостаточным.
  • При этом последний представляет собой относительно древнего «динозавра» (2003 год) и доступен только в виде zip-архива.
  • Публичный контроль версий присутствует только у первых четырех. При этом на GitHub размещены репозитории только первых трех. Репозиторий Dynatree расположен на Google Code. Тут следует отметить, что судя по всему Dynatree эволюционирует в проект Fancytree, который расположен уже на GitHub.
  • Ни один из проектов не включает NuGet-пакет.

Взвесив все «за» и «против», я решил изобрести собственный велосипед со следующими характеристиками: максимально простой, дружественный к стандартному шаблону приложения на базе ASP.NET MVC и Visual Studio 2012, без груза поддержки устаревших браузеров. Велосипед был изобретен. Его отличительными особенностями стали:
  • реализация на базе TypeScript и LESS,
  • работа с информационной моделью дерева в соответствии с шаблоном Composite.

Для изолирования CSS правил используется простой, но, на мой взгляд, наиболее правильный подход.
  1. К элементу-контейнеру дерева добавляется имя класса, соответствующее как бы namespace'у.
  2. Селекторы всех CSS правил для компонента контекстно-дочерние, где в качестве единственно обязательного корневого родителя указан класс из предыдущего пункта.
Таким образом, CSS правила можно построить на основе селекторов тегов, не создавая для каждого элемента специальный класс. Использование LESS делает исходный код таких CSS правил довольно наглядным.
LESS CSS
.resnyanskiy-tree >
ul.container {
  border: 1px dotted gray;
  font-family: Tahoma;
  font-size: 10pt;
  padding: 1px;
  li {
    list-style-type: none;
    background-image: url(images/vline.png);
    background-repeat: repeat-y;
    ul {
      padding-left: 16px;
    }
    span {
      span {
        height: 16px;
      }
    }
...
}
.resnyanskiy-tree > ul.container {
  border: 1px dotted gray;
  font-family: Tahoma;
  font-size: 10pt;
  padding: 1px;
}
.resnyanskiy-tree > ul.container li {
  list-style-type: none;
  background-image: url(images/vline.png);
  background-repeat: repeat-y;
}
.resnyanskiy-tree > ul.container li ul {
  padding-left: 16px;
}
.resnyanskiy-tree > ul.container li span span {
  height: 16px;
}
...
Основной TypeScript-класс Tree имеет три открытых члена.
  • Метод updateNode(...) добавляет элементы в указанный узел.
  • Свойства onBranchExpand и onNodeClick используются для указания обработчиков соответствующих событий.
Два закрытых метода renderNodeItemsTo(...) и toggleNodeItemsVisible(...) реализуют основную логику. Изменение видимости элементов дерева реализуется посредством добавления/удаления DOM-элементов (для удаления используется Element.removeChild(...)jsperf).
Метод toggleNodeItemsVisible(...) возвращает false, если указанный узел дерева (JS-объект) не имеет элементов. Благодаря этому условие вызова обработчика onBranchExpand выглядит довольно лаконично:

if(!this.toggleNodeItemsVisible(node) && (this.onBranchExpand instanceof Function)) {
  this.onBranchExpand(nodeId);
}

Использование TypeScript+LESS при реализации компонента помимо прочего существенно упрощает его применение в своих проектах и доработку под свои нужды.

GitHub — github.com/resnyanskiy/js.tree
NuGet — nuget.org/packages/resnyanskiy.js.tree
Tags:
Hubs:
+3
Comments 1
Comments Comments 1

Articles