Vue. Дерево (TreeView) - insbor.ru
insbor.ru

insbor.ru

Привет, это я

Читаю, пишу, перечитываю и исправляю.


Что здесь происходит


Предыдущие записи


Vue. Дерево (TreeView)

Опубликовано :   |  Кем :   |  Категория :  Vue

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

Начнём с шаблона:

На всякий случай напомню пару моментов о шаблонах Vue:

  • Если требуется выводить несколько элементов с одинаковыми условиями типа v-if или v-show, а обёртывать в общий блок не хочется, или нельзя, их можно поместить в тег template, которому и назначется общее условие видимости.
  • Один и тот же тег ref="refName" может быть назначен нескольким элементам шаблона, они будут доступны по индексу в массиве: this.$refs.refName[index].
  • Обработчик события (например, @click="method") вызовет метод, передав в него объект события клика, но @click="method(params)" вызовет метод с переданными параметрами (выполнит выражение javascript).
  • В документации к Vue2 вроде бы писали, что корневым элементом шаблона могут быть два блока с условиями v-if / v-else, чтобы в каждый момент был выведен только один из них, но у меня так не получилось.
  • В новых версиях Vue при попытке использовать this.value в качестве v-model для дочерних элементов будет вызвано предупреждение о том, что такое поведение нежелательно. Да и ожидаемого результата вы не добьётесь. Всё взаимодействие компонентов с родителями должно осуществляться с помощью событий, а для передачи значения дочерним элементам можно или создать вычисляемое свойство на основе this.value с возможностью записи, или создать внутреннее свойство myInnerValue, инициализировав его значением value и добавить на this.value наблюдатель (watch) с обновлением this.myInnerValue.
  • Начиная с версии Vue 2.2 в циклах v-for параметр key становится обязательным. Ещё совсем недавно это было не так. 
  • Если в шаблоне нужно указать тег компонента, который к этому моменту ещё не зарегистрирован, или не может там быть по другим причинам, можно использовать подходящий тег, указав в нём is="needed-tag"

Итак, что происходит в шаблоне? Извне в компонент tree-view приходит параметр nodes c массивом корневых узлов дерева. Если это свойство задано, компонент определяет свой экземпляр как корневой (иначе - как узел), перечисляет все узлы и отрисовывает, передавая в параметр node каждого из них объект, имеющий свойства id, name и массив дочерних узлов children, которые тоже отрисовываются как дочерние компоненты.

Вычисляемые свойства isNode, isRoot, isOpen, hasChildren, childrenExists в комментариях не нуждаются. Свойство selectedId инициализуется значением this.value и содержит ID выбранного узла во всём дереве. Передать вниз по дочерним узлам обновлённое значение легко - достаточно указать его как их v-model="selectedId". Однако, при изменении value у дочернего элемента, value родителя автоматически не изменится, значит нужно обновить его вручную, отправив родителю нужное событие.

В принципе, основа готова. Сейчас входные данные ожидаются в виде уже сформированного объекта со всеми вложенными узлами. Не так уж и трудно добавить их загрузку только при первом открытии вложенных деревьев, редактирование, создание и удаление узлов. В последнем случае придётся вновь обращаться к родительскому элементу для того, чтобы удалить текущий узел. Очень хорошо так же будет реализовать перемещение поддеревьев перетаскиванием (я пока поленился). Как обрабатывать события drag-n-drop, рассказывается в прошлой заметке.

В своей реализации (демо) я добавил флаг readonly и три параметра - nodesUrl, saveUrl и deleteUrl для загрузки списка дочерних узлов, создания/редактирования новых узлов и их удаления на сервере. Исходный код компонента и серверной части на основе списка категорий товаров Яндекс.Маркет доступен на github. Вот, как это выглядит:

На этот раз советовать что-то конкретное нет смысла - у Vue прекрасная документация. Могу разве что выделить github.com/vuejs/awesome-vue со списком полезных ресурсов и компонентов, официальный пример реализации дерева и небольшую запись по шаблонам компонентов.