The ability to order lists is a popular feature for many web apps. We want to accomplish it with a minimum of overheads, dependencies and fuss. With Ruby on Rails and modern browsers - this is a simple task.
Step 1, The Setup.
We will need to manage the ordering of the items in the database. Rails has a gem for that. Add https://github.com/swanandp/acts_as_list to your gemfile.
This acts_as extension provides the capabilities for sorting and reordering a number of objects in a list.
The class that has this specified needs to have a position column defined as an integer on the mapped database table. Now add the position integer to the model we are ordering
class AddPositionToSpans < ActiveRecord::Migration[5.2]
def change
add_column :spans, :position, :integer
end
end
 Add the macro to the model being ordered. Lists can be scoped to their belongs_to record. This means the list order numbers will only be updated in relation to other records that belongs_to their circuit in this example.
acts_as_list scope: :circuit
default_scope { order(position: :asc) }
If you have existing records, it's now a good time to set the order to start with. A migration is good for this.
class SetSpanLists < ActiveRecord::Migration[5.2]
def change
Circuit.all.each do |circuit|
circuit.spans.order(:external_reference).each.with_index(1) do |span, index|
span.update_column :position, index
end
end
end
end
Step 2. The Front End.
Rather than using a large library like JqueryUI for this, we can now use this simple plugin that gets the job done with a lot less code and overheads.
Lightweight vanillajs micro-library for creating sortable lists and grids using native HTML5 drag and drop API.
https://github.com/lukasoppermann/html5sortable
Download a copy of the js from this repo, I like to use the uncompressed dist copy as Rails will compress the code for production and it means I'm dealing with js code I can read in case something goes wrong and I need to understand what's happening.
Step 3, Hooking It All Together.
Now we need to add some code to tell the plugin which list is orderable and what to do when the list is reordered. Add this to the table:
<tbody id= "spans" data-url = "<%= sort_spans_path %>">
And then this to your applications js file.
$(document).on('turbolinks:load', function() {
sortable('#spans', {
items: 'tr'
});
if (typeof sortable('#spans')[0] != 'undefined'){
sortable('#spans')[0].addEventListener('sortupdate', function(e) {
var dataIDList = $(this).children().map(function(index){
$(this).find( ".position" ).text(index + 1)
return "span[]=" + $(this).data("id");
}).get().join("&");
Rails.ajax({
url: $(this).data("url"),
type: "PATCH",
data: dataIDList,
});
});
}
})

This tells the plugin:
- That the table body with id "spans" is orderable.
- Listens to the sortable for spans to update
- Builds a serialised list of the span ids
- Sends the list to the controller action for saving.
Step 4, The Final Bit.
So now we need to create a route and a controller method so we can patch the data to it and update the records. Add this to your routes:
resources :spans do
collection do
patch :sort
end
end
Now, add this, in it's simplest form, this to your controller:
def sort
params[:span].each_with_index do |id, index|
Span.where(id: id).update_all(position: index + 1)
end
respond_to do |format|
format.js
end
end
Finally, you will want some visual queue that the update has happened, so add a sort.js.rb to the spans/views
$("#span-messages").html('Spans updated');
$("#span-messages").highlight(1000);
And ta da. A sortable list. Acts as list has a lot of nice methods to navigate the list and move items around. Html5Sortable can use nested lists, choose placeholders and drag handles and accept max item numbers etc. It's pretty flexible. Happy hacking.
Leave a Reply
Comments
No comments on this article.
comments powered by Disqus