activerecord-sortable
The gem allows you to integrate jQuery UI Sortable with your models in Ruby on Rails app: for example, you'll be able to create admin page to arrange some items using drag and drop.
How it works
- Order kept using values of integer column,
position
by default. - To retreive instances in order, auto created scope,
ordered_by_position_asc
by default. - All integer column values shifted for affected records on destroy some other record.
- Auto created
move_to!
, wich allows to move instance to specified position. - Any position column changes affects
updated_at
andupdated_on
column values. - Gem includes jQuery-plugin to easily integrate drag and drop with
move_to!
(see example below).
Example
# Gemfile
gem 'activerecord-sortable'
gem 'jquery-ui-rails' # if you plan to use drag and drop
# app/models/thing.rb
class Thing < ActiveRecord::Base
acts_as_sortable
end
# db/migrate/20140512100816_create_things.rb
class CreateThings < ActiveRecord::Migration
def change
create_table :things do |t|
t.integer :position, :null => false
t.timestamps
end
add_index :things, [:position]
end
end
# config/routes.rb
Dummy::Application.routes.draw do
resources :things, :only => [:index] do
member do
post :move
end
end
root 'things#index'
end
# app/controllers/things_controller.rb
class ThingsController < ApplicationController
def index
@things = Thing.ordered_by_position_asc
end
def move
@thing = Thing.find(params[:id])
@thing.move_to! params[:position]
end
end
<!-- app/views/things/index.html.erb -->
<h1>Sortable thing</h1>
<p>Use drag and drop to sort things, reload page, notice order kept.</p>
<ol data-role="activerecord_sortable">
<%= render @things %>
</ol>
<!-- app/views/things/_thing.html.erb -->
<!-- required attributes are data-role, data-move-url, data-position -->
<li data-role="thing<%= thing.id %>" data-move-url="<%= move_thing_url(thing) %>" data-position="<%= thing.position %>">
<h2>Thing <%= thing.id %></h2>
</li>
// app/views/things/move.js.erb
var node = $('*[data-role="thing<%= @thing.id %>"]');
var new_node_html = '<%= j render @thing %>';
node.replaceWith(new_node_html);
// app/assets/javascripts/application.js
//= require jquery
//= require jquery_ujs
//= require sortable
//= require jquery-ui/sortable
$(document).ready(function(){
$('*[data-role=activerecord_sortable]').activerecord_sortable();
});
See also dummy app code.
Settings
class Thing < ActiveRecord::Base
acts_as_sortable do |config|
# which relation use to keep instances in order
# this setting is useful in STI models case, for example,
# to use order in one STI-class scope use
# config[:relation] = ->(instance) {instance.class.base_class)}
#
# using all instances of model by default
config[:relation] = ->(instance) {instance.class}
# append new instances to relation on create
# prepend by default
config[:append] = false
# integer column to specify order
# position by default
config[:position_column] = :position
# touch other members of relation on prepend
# true by default
config[:touch] = true
end
end
JavaScript Events and Settings
Is example:
// app/assets/javascripts/application.js
//= require jquery
//= require jquery_ujs
//= require sortable
//= require jquery-ui/sortable
$(document).ready(function(){
$('*[data-role=activerecord_sortable]').activerecord_sortable();
});
$('*[data-role=activerecord_sortable]')
triggers events:
-
sortable:start
– change position ajax request sended to server -
sortable:stop
– server respond to ajax request -
sortable:sort_success
– server respond successfully to change position ajax request -
sortable:sort_error
– server respond with error to change position ajax request
Before send ajax request to server jQuery UI Sortable disabled, after receive response enable.
activerecord_sortable()
accetps JQuery UI Sortable-style options. By default axis
uses y
value (to prevent horizontal dragging), and update
overwrites by internal handler and is unavailabe to set.
How to add activerecord-sortable into existing model
# Gemfile
gem 'activerecord-sortable'
# db/migrate/20140525112125_add_position_to_items.rb
class AddPositionToItems < ActiveRecord::Migration
def up
add_column :items, :position, :integer
# specify order
Item.order('id desc').each.with_index do |item, position|
item.update_attribute :position, position
end
change_column :items, :position, :integer, :null => false
add_index :items, [:position]
end
def down
remove_column :items, :position, :integer
end
end
# app/models/item.rb
class Item < ActiveRecord::Base
acts_as_sortable
end
activerecord-sortable for nested models
# app/models/parent.rb
class Parent < ActiveRecord::Base
has_many :children, -> { ordered_by_position_asc }
accepts_nested_attributes_for :children
end
# app/models/child.rb
class Child < ActiveRecord::Base
acts_as_sortable do |config|
config[:relation] = ->(instance) {instance.parent.children}
end
belongs_to :parent
end
# app/controllers/parents_controller.rb
class ParentsController < ApplicationController
def new
@parent = Parent.new
3.times do |position|
@parent.children.build(
:name => "Child #{position}",
:position => position # required
)
end
end
def create
@parent = Parent.new parent_params
if @parent.save
redirect_to parent_path(@parent)
else
render :new
end
end
def show
@parent = Parent.find(params[:id])
end
private
def parent_params
params.require(:parent).permit(:children_attributes => [:name, :position])
end
end
<!-- app/views/parents/new.html.erb -->
<h1>New parent</h1>
<%= render :partial => 'form' %>
<!-- app/views/parents/_form.html.erb -->
<%= form_for @parent do |f| %>
<fieldset>
<legend>Children</legend>
<ol data-role="activerecord_sortable">
<%= f.fields_for :children do |ff| %>
<!-- both data-* attributes required -->
<li data-role="child<%= ff.object.to_s %>" data-position="<%= ff.object.position %>">
<%= ff.object.name %>
<!-- to pass position into controller's code -->
<%= ff.hidden_field :position, :data => { :role => 'position' } %>
</li>
<% end %>
</ol>
</fieldset>
<%= f.submit %>
<% end %>
Note on Patches / Pull Requests
- Fork the project.
- Make your feature addition or bug fix.
- Send me a pull request. Bonus points for topic branches.
License
activerecord-sortable is free software, and may be redistributed under the terms specified in the LICENSE file.