gem the_sortable_tree

TheSortableTree — гем, реализующий функционал Drag&Drop для деревьев построенных на основе гемов awesome_nested_set или nested_set.



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

Мы использовали гем для формирования многоуровневого дерева комментариев. Но было одно но… В тот момент не было ни одного хелпера, который бы позволял отрисовывать эти деревья. Из-за этого приходилось выдумывать свои велосипеды. Свой велосипед сделал и я.

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

Да, меня критиковали — это медленно отрисовывается, это не сработает на дереве из 10 000 элементов, надо по-другому и вообще…

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

И вот я, потупив взор и нервно теребя в руках носовой платочек, представляю вам реинкарнацию моего хелпера в виде гема основанного на Rails Engines.

Уверен, для отрисовки небольших деревьев (до 100 элементов) и создания небольших CMS, где хочется управлять деревом простым перетаскиванием, мой хелпер подойдет идеально.

https://github.com/the-teacher/the_sortable_tree



На данный момент предусмотрено два варианта отрисовки:

  1. Сортируемое дерево (Административный интерфейс)
  2. Обычное дерево (Пользовательский интерфейс)


Примерно так выглядит дерево для пользователя



Установить гем и начать его использовать очень просто.

Дополните свой Gemfile. Нам потребуется HAML, Awesome nested set и сам хелпер отрисовки the_sortable_tree.

gem 'haml'
gem 'awesome_nested_set' # gem 'nested_set'
gem 'the_sortable_tree'


Я специально убрал жесткую зависимость от HAML, чтобы фанаты ERB или SLIM смогли тоже использовать хелпер, но вам ребята придется самостоятельно переконвертировать вьюшки из хамла.

Проверим, что все JS библиотеки подключены

app/assets/javascripts/application.js

//= require jquery
//= require jquery-ui
//= require jquery_ujs


Рассмотрим использование гема на примере модели Page.

Добавим в модель скопы

class Page < ActiveRecord::Base
  acts_as_nested_set
  include TheSortableTree::Scopes
end


В контроллер добавим функцию перестроения дерева

class PagesController < ApplicationController
  include TheSortableTreeController::Rebuild
end


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

class PagesController < ApplicationController
  include TheSortableTreeController::ReversedRebuild
end


Дополните роуты.

Один action нам потребуется, чтобы отобразить дерево. Как вы его назовете — не важно. У меня это :manage.

rebuild — метод перемещающий элемент дерева из одной позиции в другую. Этот метод зашит в гем — он обязателен.

resources :pages do
  collection do
    get :manage
    post :rebuild
  end
end


Завершим код контроллера.

Получим все дерево (прямой порядок выборки)

class PagesController < ApplicationController
  include TheSortableTreeController::Rebuild

  def manage
    @pages = Page.nested_set.all
  end
end


Обратный порядок выборки

class PagesController < ApplicationController
  include TheSortableTreeController::ReversedRebuild

  def manage
    @pages = Page.reversed_nested_set.all
  end

end


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

- content_for :css do
  = stylesheet_link_tag 'the_sortable_tree', :media => :screen
- content_for :js do
  = javascript_include_tag 'jquery.ui.nestedSortable'

= sortable_tree @pages, :new_url => new_page_path, :max_levels => 4


Или отрисуем дерево для пользователя.

- content_for :css do
  = stylesheet_link_tag 'the_sortable_tree_min', :media => :screen

= sortable_tree @pages, :new_url => new_page_path, :path => 'the_sortable_tree_min'


Для того, чтобы content_for :css и content_for :js сработали, добавьте в layouts/application.xxx yield(:js) и yield(:css).

Конечно, то, как вы будите подключать js и css — это ваше дело.

Всё! Дерево должно отображаться и перетаскиваться.

Если что-то не получается — скачайте и запустите LiveDemo.

Кастомизация отображения


Запустите генератор вьюшек и укажите ему название контроллера.

rails g the_sortable_tree:views pages 


Для кастомизации пользовательского дерева укажите параметр min

rails g the_sortable_tree:views pages min


Отредактируйте вьюшки и укажите хелперу, где они находятся:

= sortable_tree @pages, :new_url => new_page_path, :path => 'pages/the_sortable_tree'


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

Надеюсь, это кому-то пригодится.

https://github.com/the-teacher/the_sortable_tree

P.S.: Если вы заметили орфографическую или синтаксическую ошибку, ошибку в коде или любую другую ошибку, пожалуйста, сообщите мне в личку, я буду вам очень благодарен. Заранее спасибо!
+23
11 февраля 2012, 11:31
68
zayko 24,0

комментарии (13)

+1
krovatti #
Давно искал что-то подобное. Благодарю!
+1
UseRifle #
Илья, спасибо за гем, ты знаешь, что я им уже пользуюсь :)

Я вот подумал насчет скорости рендеринга — а может быть есть смысл от сервера данные клиенту в json отдавать, а весь рендеринг осуществлять в клиенте?
+1
inossidabile #
А для простоты переноса можно использовать tilt и coffee-haml
+1
zayko #
google://tilt

Ваш коммент мне гугл сломал!
А вы знали про эту пасхалку гугла?
+1
inossidabile #
Хех, нет, не знал :). Мило :)
+1
zayko #
Я специально не использовал coffee и sass, чтобы было как можно меньше завязок на частные решения. Главная задача проекта — дать посыл к развитию данной темы. Увы за несколько лет её так никто и не разработал.

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

Надо еще подумать что переносить и куда.
+1
inossidabile #
Эти частные решения встроены в современные рельсы. А вот haml, который так туда и не попал в вашем примере присутствует :). Ваше дело, но я не понимаю этой логики.
+1
zayko #
Я использую sublime. Писать на хамле быстрее чем на erb и код намного легче читается. Slim меня не устаревает, ввиду его требованиям к форматированию кода и параметров. Это вопрос исключительно личных предпочтений. Писать вьюхи на erb после 3 лет опыта работы с хамлом невыносимо. Вот почему хамл.

Завязывать гем на sass и coffee не вижу смысла. Там всего строчек 20. Кому надо за 10 минут сделают так как им надо. А для людей с нулевым опытом в разработке это может быть пугающим — пусть в репе они увидят понятный им css и js код.

На самом деле такие мелочи — не стоят потраченного на них времени.
+1
zayko #
Самый медленный кусок, конечно, паршелы. Ты знаешь, Миша, а в этом что-то есть.
Думаю, такую возможность имеет смысл сделать в качестве одного из вариантов. Как основной подход я бы не стал это использовать. Если это будут комменты — то асинхронность убьет индексацию поисковиком. Как с этим быть?

Даже на хабре больше 200 комментов — это не постоянное явление. Паршелами это отрисуется сек за 15 + кеширование.

Для административных частей — да. Надо попробовать. Отличная идея!
+1
Asm #
А не думали написать с использованием ancestry?
0
zayko #
обсуждали. но пока не было необходимости
+2
SkyEagle #
А не желаете сделать тоже самое в связке с Dynatree? В любом случае спасибо.
0
zayko #
Первый раз вижу, если честно. По сути главное повесить обработчик onUpdate, собрать и отправить на сервер 4 параметра — id, parent_id, prev_id, next_id. Если это можно достаточно просто сделать — до привязка не должна вызвать трудностей. Но я не думаю, что смогу в ближайшее время этим заняться.

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