yamarkz's blog

紫陽花

acts_as_listとjQueryで並べ替えを実装する

jQueryのsortableを用いて並べ替えを実装する Rails

acts_as_list

モデルの並び順を簡単に操作できるようにするGem

昔のRails1系あたりでは標準で搭載されていたらしいが、 途中でGemとして切り離されて現在は独立したGemとして存在する。

Rails 1.x の時代には、Rails 本体に組み込まれていたけれど、その後プラグインとして分離されました。

引用:acts_as_list: gem か plugin か

本家がこちら。 https://github.com/swanandp/acts_as_list

導入の仕方などはドキュメントにわかりやすく書かれているのでそこを参考すると良さそう。 acts_as_listでは positionカラム がデータのソート基準となるよう決まっている。 なのでacts_as_listを使いたい場合は、対象とするテーブルのカラムにposition絡むを追加しましょう。

# has_manyでtodo_itemsを持つ親モデル
class TodoList < ActiveRecord::Base
  has_many :todo_items, -> { order(position: :asc) }
end

# belongs_toでtodo_listに紐づく子モデル
class TodoItem < ActiveRecord::Base
  belongs_to :todo_list
  # scopeで定義する
  acts_as_list scope: :todo_list
end

todo_list = TodoList.find(...)
# move_to_bottomで値を操作1,2,3とあるならば1が3になり2,3が繰り上がる
todo_list.todo_items.first.move_to_bottom
# move_higherで1,2,3,とあるならば2,1,3と変更される
todo_list.todo_items.last.move_higher

また、最近の紹介記事ですと、Qiitaに上がっているjnchitoさんの記事が素晴らしくわかりやすかったです。 実装の際に参考にさせてもらいました。 Rails 4で作るドラッグアンドドロップで表示順を変更できるサンプルアプリ(スクリーンキャスト付き)

動画は見ていませんが初心者にもわかりやすいように筆者が参考にした記事や、 動画を用いての実装方法まで紹介していてさすがですね。

今回自分は ranked_model を使った実装を行っていないので機会があればそちらも使ってみたいなと思います。 どうやらranked-modleの方がパフォーマンスが良いようです。(検証はしてません)

Railsで順番を管理するgemとして、ranked-model gemを使います。 acts_as_listも有名ですが、ranked-modelの方がパフォーマンス的に優れています。

ドラッグ&ドロップで並べ替えを行う実装

jQueryのsortableを用いて、ドラッグ&ドロップで操作を行う実装を紹介します。 上記でacts_as_listを紹介しましたが、今回の実装では用いません。 acts_as_listではなくjQueryのsortableを用いることでも並べ替えを良い感じにできるんだぜってことを紹介したいと思います。

# Railsで良い感じに実装するためのGem達
gem 'jquery-ui-rails'
gem 'jquery-turbolinks'
// application.jsで読み込みます
//= require jquery-ui/effect-highlight
//= require jquery-ui/sortable

D&Dを行った際の処理 $('.table-sortable').sortable でsortableを適用させる。 D&Dをトリガーにしてajax処理を行いDB側のposition値を変更する使用

// table_sort.js
$(function() {
  return $('.table-sortable').sortable({
    axis: 'y',
    items: '.sentence',
    update: function() {
    // ajax処理で並べ替え後の値をサーバー側に渡す
      $.ajax({
        type: 'POST',
        url: '/sort',
        dataType: 'json',
        // $('.table-sortable').sortable('serialize') で変更後の値を取得
        data: $('.table-sortable').sortable('serialize')
      });
    },
    // ここはおまけで変更完了エフェクトを付け足す
    stop: function(e, ui) {
      return ui.item.children('td').effect('highlight');
    }
  });
});

controller側の実装

def sort
  Content.all.each do |sentence|
    content.position = params[:content].index(content.position.to_s) + 1
    content.save
  end
  render nothing: true
end

model側での実装 値を追加した際にpositionカラムに値の最大値をセットするために before_createにset_positionメソッドを定義します

  before_create :set_position

  def set_position
    if max =Content.where(hoge_id: hoge_id).maximum(:position)
      self.position = max + 1
    end
  end

slimの実装 id="position_#{content.position}"でデフォルト値をセット

table.table.table-striped.table-sortable
  thead
    tr
      th id
      th name
      th description
  tbody
    - @contents.each do |content|
      tr.sontent id="position_#{content.position}"
        td= content.id
        td= content.name
        td= content.description

簡単にイケてる実装ができて便利!

参考

acts_as_list: gem か plugin か

Rails 4で作るドラッグアンドドロップで表示順を変更できるサンプルアプリ(スクリーンキャスト付き)

Ruby/Ruby on Rails/acts_as_list

[Rails]acts_as_listでモデルオブジェクトの並び替え