Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the simplest way to allow a user to drag and drop table rows in order to change their order?

I have a Ruby on Rails application that I'm writing where a user has the option to edit an invoice. They need to be able to reassign the order of the rows. Right now I have an index column in the db which is used as the default sort mechanism. I just exposed that and allowed the user to edit it.

This is not very elegant. I'd like the user to be able to drag and drop table rows. I've used Scriptaculous and Prototype a bit and am familiar with them. I've done drag and drop lists, but haven't done table rows quite like this. Anyone have any suggestions for not only reordering but capturing the reorder efficiently?

Also, the user can dynamically create a new row in JS right now, so that row has to be reorderable as well.

Bonus points if it can be done with RJS instead of direct JavaScript.

like image 712
Mark S. Avatar asked Oct 03 '08 16:10

Mark S.


3 Answers

I've used the Yahoo User Interface library to do this before:

http://developer.yahoo.com/yui/dragdrop/

like image 200
bobwienholt Avatar answered Nov 03 '22 09:11

bobwienholt


MooTools sortables are actually better than script.aculo.us because they are dynamic; MooTools allows the addition/removal of items to the list. When a new item is added to a script.aculo.us sortable, you have to destroy/recreate the sortable to make the new item sortable. There'll be a lot overhead in doing so if the list has many elements. I had to switch from script.aculo.us to the more lightweight MooTools just because of this limitation and ended up being extremely happy with that decision.

The MooTools way of making a newly added item sortable is just:

sortables.addItems(node);
like image 28
Ates Goral Avatar answered Nov 03 '22 10:11

Ates Goral


Okay, I did some more scouring and figured out something that seems to mostly be working.

edit.html.erb:

...
<table id="invoices">
   <thead>
     <tr>
      <th>Id</th>
      <th>Description</th>
     </tr>
   </thead>
   <tbody id="line_items">
      <%= render :partial => 'invoice_line_item', :collection => @invoice.invoice_line_items.sort %>
   </tbody>
</table>

<%= sortable_element('line_items', {:url => {:action => :update_index}, :tag => :tr, :constraint => :vertical}) -%>
...

app/controllers/invoices.rb

...
def update_index
  params["line_items"].each_with_index do |id, index|
    InvoiceLineItem.update(id, :index => index)
  end
  render :nothing => true
end
...

The important part is :tag => :tr in "sortable_element" and params["line_items"] -- this gives the new list of ids and is triggered on the drop.

Detriments: Makes the AJAX call on drop, I think I'd prefer to store the order and update when the user hits "save". Untested on IE.

like image 2
Mark S. Avatar answered Nov 03 '22 10:11

Mark S.