I want to create draggable ordered lists but according to this blog the acts_as_lists gem is very server intensive and it also says build failing. They suggest the ranked model gem however it appears to be deprecated.
So what is the most efficient way to do this without causing a huge load on the server when there are many users rearranging lists.
I use HTML5 Sortable jQuery Plugin.
For each item, there is a ordering field to specify the ordering (1 to n). Once the draggable sorting is done by user, the ordering is posted to Rails backend in array, e.g. [1, 5, 3, 6, 2, 4]. The number in array is id of items. Based on this array, I can update the ordering field. For example, the first item (id = 1) in array has ordering 1, and second item (id = 5) has ordering 2. Later, you can use order("ordering ASC") for ordering.
First, there is a sort view (route is get 'items/sort') for user to do draggable sorting. It look like this (in slim format):
div#items-sortable
- @items.each do |item|
= div_for item, data: {id: item.id}
= item.title
hr
= form_tag action: :ordering do
= hidden_field_tag :items_ids, ""
= submit_tag 'Save Changes'
The form is to keep the ordering array and allows data to be posted to Rails.
The Javascript (CoffeeScript) looks like this:
sortable_element = document.getElementById('items-sortable')
if sortable_element
$(sortable_element).sortable({
placeholder: "<div style='border-style: dashed'> </div>"
})
.bind('sortupdate', (e, ui) ->
orders = ui.endparent.children().map( -> return $(this).data("id") ).toArray()
document.getElementById('items_ids').value = JSON.stringify(orders)
)
When user does the sorting, it keeps the hidden field of form updated with ordering of item id.
Once the sorting is done and user click "Save Changes" button, it is posted to route items/ordering with item_ids parameter. The method in controller looks like this:
def ordering
if (@orders = params[:items_ids]) && (@orders.present?)
@orders = JSON.parse(params[:items_ids])
if @orders.kind_of?(Array) && @orders.size > 0
# Let build hash for mass update
data = {}
@orders.each_with_index do |iden, index|
data[iden] = {ordering: index}
end
Item.all.update(data.keys, data.values) # still update one-by-one at backend
end
end
respond_to do |format|
format.html { redirect_to items_path }
end
end
If you don't want to use form, you can try to convert it with AJAX.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With