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:
- The front-end uses jQuery UI sortable to allow drag-and-drop reordering of DOM elements
- 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.)