Creating sortable lists in Ruby on Rails isn’t difficult, but all of the tutorials I’ve found out there save the new order of the items immediately to the database. I want to only save the new sort order when the associated record is saved. Here’s some notes on how to make that happen.

Note that this is not a detailed walk-through, as I don’t have a public Rails server lying around at the moment (it’s on my to-do list!).

Sortable Lists

I can’t improve on the Sortable Lists Railscast by Ryan Bates. So that’ll be your starting point.

The Problem

In the partial which builds the sortable list, the Ruby command is as follows;

<%= sortable_element("faqs", :url => sort_faqs_path, :handle => "handle") %>

This calls the sort controller for faqs when the items are dropped into place, and the controller then immediately saves the record to the database. This is ideal for a lot of cases, but on a CMS where users are tasked with ordering the lists there will be several times when they suddenly forget what they’re doing, and they want to start again. The above solution doesn’t give them that choice, but with a few modifications, we can get there.

The Changes

Change the file faqs/index.html.erb to the following. Basically, we’re adding a javascript function, adding a hidden field storing the position, and modifying the sortable_element call.

 

<!-- faqs/index.html.erb -->
 <script type="text/javascript">
     function update_sort() {
         var arr = document.getElementsByClassName('kp_sequence');
         // Gives us each sequence element as an array

         for (var index =0; index < arr.length; ++index) {
             var item = arr[index];
             var newindex = index+1;// Because index starts from 0
             item.value = newindex;  
         }
     }

 </script>
<ul id="faqs">
<% for faq in @faqs %>
  <% content_tag_for :li, faq do %>
    <span class="handle">[drag]</span>
    <%= link_to h(faq.question), faq %>
    <%= hidden_field :position, :class=> 'kp_sequence' %>
 <% end %>
<% end %>
</ul>
 <%= button_to "Update", :action => "update" %>

<%= sortable_element('faqs', :onUpdate => 'function(){update_sort();}',  :tag => "handle" ) %>

That’s all there is to it.

The user can now sort the list in any order they wish, and you will see the ‘value’ for the position field being updated as necessary; but that new value won’t be saved to the database until the update method of the controller is called – usually by an update button.

How it works

We’ve added a hidden field which contains the current value of the position column. We’ve given this field a class of ‘kp_sequence’. The sortable_element function has an event of ‘onUpdate’ which is fired once an element in the sortable list is dropped into place. This calls the javascript which runs through all the items with a class of ‘kp_sequence’ and reorders them on the page. The Update method of the controller will automatically save the new values of sequence.