Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implement ajax sortable lists with jQuery and Rails 3

There are many similar questions but I can't find one that exactly meets my needs.

You'd think this is a very common problem and there should be a polished gem solutions out there.

I want to reorder list and save position of each item to the database with ajax very much like the to-do items in a list in Basecamp. Although it would be nice to be able to move items from one level to another in nested lists I don't really need that feature at all.

I'm using Rails 3.1, jQuery. It would make sense to integrate the solution with the sortable jQuery plugin but I'm open to any solutions.

If you don't know of any ready solutions, can you give me pointers on how to go about it.

My apps used to use the acts_as_category plugin but it's not being maintained and I have implemented all the other tree features manually.

like image 685
allesklar Avatar asked Oct 05 '11 16:10

allesklar


1 Answers

There is a decent blog post on this here:

was webtempest.com/sortable-list-in-ruby-on-rails-3-almost-unobtrusive-jquery/

Still shows at http://web.archive.org/web/20120315004343/http://webtempest.com/sortable-list-in-ruby-on-rails-3-almost-unobtrusive-jquery

Essentially:

  1. The front-end uses jQuery UI sortable to allow drag-and-drop reordering of DOM elements
  2. The back-end uses acts_as_list to handle updating the database

These both seem reasonably robust, and I was able to implement a variation on the basic functionality outlined, with new item creation on the same screen and some CSS 3 bells and whistles (just style the .your-class.ui-sortable-helper appropriately) without much fuss. I haven't tested extensively across browsers, but it seems happy in WebKit and Firefox.

The example on the blog doesn't really use acts_as_list much – it just serialises the object IDs using jQuery and then iterates over them in the controller directly – but I guess it's useful having those functions on the back end if you need to automate changes from there for some reason.

Key code from the blog post:

Javascript:

$(document).ready(function(){
  $('#books').sortable({
    axis: 'y',
    dropOnEmpty: false,
    handle: '.handle',
    cursor: 'crosshair',
    items: 'li',
    opacity: 0.4,
    scroll: true,
    update: function(){
      $.ajax({
        url: '/books/sort',
        type: 'post',
        data: $('#books').sortable('serialize'),
        dataType: 'script',
        complete: function(request){
          $('#books').effect('highlight');
        }
      });
    }
  });
});

View:

<li id="book_<%= book.id %>">

This includes an id like book_5, which allows $('#books').sortable('serialize') in the Javascript to create a query parameter which Rails can parse.

Controller:

def sort
  @books = Book.all
  @books.each do |book|
    book.position = params['book'].index(book.id.to_s) + 1
  book.save
end

This might not be appropriate, depending on how your model is scoped / access-controlled. In my own solution I iterated over the params['book'] instead, and included some checking/error-handling to ensure only meaningful values would be accepted.

(P.S. this is quite similar to the approach Ryan Bates gives in his, paywalled, videocast on the same topic.)

(P.P.S. I am aware this is an old question, but, as so often with StackOverflow, Google got me here so I thought I would document what I did.)

like image 101
Leo Avatar answered Nov 13 '22 22:11

Leo