Web team of a customer was asking me for a fast solution to drag'n drop sort their categories of products on their admin dashboard. Here's my solution working fast and perfectly without lots of code.
Here's the closure_tree gem, making models acting as nodes in a tree data structure : https://github.com/ClosureTree/closure_tree
And here's nestedSortable for the JS part.
Here's my view :
<ul class="sort" data-url="<%= sort_admin_catalog_categories_path %>">
<% categories.each do |c| %>
<%= content_tag_for :li, c do %>
<ul>
<%= c.name.upcase %>
<% if c.child? then %>
<% c.children.each do |c1| %>
<%= content_tag_for :li, c1 do %>
<ul>
<strong><%= c1.name %></strong>
<% if c1.child? then %>
<% c1.children.each do |c2| %>
<%= content_tag_for :li, c2 do %>
<%= c2.name %>
<% end %>
<% end %>
<% end %>
<% end %>
</ul>
<% end %>
<% end %>
</ul>
<% end %>
<% end %>
JS :
$(document).ready(function() {
$('.sort').nestedSortable({
items: 'li',
update: function(e, ui) {
Rails.ajax({
url: $(this).data("url"),
type: "PATCH",
data: $(this).nestedSortable('serialize'),
});
}
});
});
Route :
resources :categories do
collection do
patch :sort
end
end
Controller :
def sort
params[:category].each_with_index do |id, index|
Category.where(id: id).update_all({sort_order: index+1})
end
head :ok
end
Notice that since Rails 5.1 you should use params[:category].to_unsafe_h.each_with_index or build a relative method for strong params
Finally the nestedSortable options :
options: {
disableParentChange: false,
doNotClear: false,
expandOnHover: 700,
isAllowed: function() { return true; },
isTree: true,
listType: "ul",
maxLevels: 0, //infinite
protectRoot: false,
rootID: null,
rtl: false,
startCollapsed: false,
tabSize: 20,
If it can help.
N.B. : I didn't solved yet the deal of N+1 if someone wants to contribute I would really appreciate.