Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the most efficient way to create draggable sortable lists in Rails?

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.

like image 701
EliteViper7777 Avatar asked Jul 10 '15 01:07

EliteViper7777


1 Answers

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'>&nbsp;&nbsp;&nbsp;</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.

like image 177
Yen-Ju Avatar answered Nov 15 '22 05:11

Yen-Ju