Railsからはじめるプログラミング日記

駆け出しプログラマー(主にRuby on Rails)の業務で役立ったコードをメモしていくブログです。自分だけでなく誰かの役に立てれば本望です。

【Rails5】acts_as_listでデータの並び替え機能を実装

一覧ページ(index)でよく使いたくなるデータの並び替え機能について

Gemをインストール

acts_as_listというgemを使います。
Gemfileに記載してbundle installしましょう。

【Gemfile】

gem 'acts_as_list'

並べ替えしたいデータのモデルを編集

Userモデルで実装するとしましょう。
userモデルに「acth_as_list」の1行を追加。

【models/user.rb】

class User < ApplicationRecord
  acts_as_list
end

これでデータの作成時にpositionカラムにデータが入るようになります。

※注意!Userテーブルのカラムにpositionがないと動きません、ない場合はrails g migrationで付け加えてください

moveアクションの追加

アクション名はなんでもいいんですが(例えばchange_positionとか)、今回はmoveで。
users_controllerにmoveアクションを定義します。
indexの@userを作る箇所ではpositionでオーダーかける必要があります。

【controllers/users_controller.rb】

class UsersController < ApplicationController
  def index
    @user = User.all.order(:position)
  end

  def move
    case params[:move]
    when 'up'
      @user.move_higher
      @target = @user.lower_item
    when 'down'
      @user.move_lower
      @target = @user.higher_item
    else
      return head :ok
    end
  end
end

moveアクションを見ると、params[:move]が飛んでくることがわかりますので、この後viewで飛ばすように実装します。
params[:move]の値がupならmove_higherで上に移動、downならmove_lowerで下に移動をしていますね。

アクション定義したのでroutesに追記

新規アクションを設定したのでroutesを変更しましょう。

【routes.rb】

Rails.application.routes.draw do
  resources :users do
    member do
      get :move
    end
  end
end

viewの編集

設定が終わったのでviewを作っていきます。
index.html.erbでユーザ一覧を確認できるようにテーブル組みで作成。
左端にはpositionの入れ替えをするlink_toを用意します。

【users/index.html.erb】

<table class="list_table mb_l">
  <tr>
    <th colspan="2"></th>
    <th><%=f User, :name %></th>
    <th><%=f User, :email %></th>
    <th><%=f User, :created_at %></th>
    <th></th>
  </tr>
  <% @users.each do |user| %>
    <tr>
      <td><%= link_to '▲', move__user_path(id: user, move: 'up'), remote: true unless user.first? %></td>
      <td><%= link_to '▼', move_user_path(id: user, move: 'down'), remote: true unless user.last? %></td>
      <td><%= user.name %></td>
      <td><%= user.email %></td>
      <td><%=l user.created_at, format: :long %></td>
      <td><%= link_to t('link.delete'), user_path(user), method: :delete, data: { confirm: 'Are you sure?' } %></td>
    </tr>
  <% end %>
</table>

@usersをeachで回したtrを入れ替えたいので部分テンプレートにします。

【users/index.html.erb】

<table class="list_table mb_l">
  <tr>
    <th colspan="2"></th>
    <th><%=f User, :name %></th>
    <th><%=f User, :email %></th>
    <th><%=f User, :created_at %></th>
    <th></th>
  </tr>
  <% @users.each do |user| %>
    <%= render 'user_list', user: user %>
  <% end %>
</table>

部分テンプレートではpositionの変更をした後に表示を切り替えるjsのトリガーを追記します。
また、jsで選択できるようにtrにidを付けておきます。

【users/_users_list.html.erb】

<tr id="user_<%= user.position %>" >
  <td><%= link_to '▲', move__user_path(id: user, move: 'up'), remote: true unless user.first? %></td>
  <td><%= link_to '▼', move_user_path(id: user, move: 'down'), remote: true unless user.last? %></td>
  <td><%= user.name %></td>
  <td><%= user.email %></td>
  <td><%=l user.created_at, format: :long %></td>
  <td><%= link_to t('link.delete'), user_path(user), method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>

表示の切替を行うjsを作成

viewの▼のリンクにremote: trueを付けました、クリックされたら呼ばれるjsを実装していきます。
まずはusers/move.coffeeファイルを作成。
その後一覧ページの表示切替の処理を記載します。

【users/move.coffee】

$("#user_<%= @user.position %>").replaceWith('<%=j render "user_list", user: @user %>')
$("#user_<%= @target.position %>").replaceWith('<%=j render "user_list", user: @target %>')

これで実装完了!

まとめ

慣れれば簡単に実装できますが、変更するファイルが多いのとcoffeeファイルを使うので初心者の方は難しく感じると思います(私もそうでした)。
ruby、railsを覚えてくると何をやっているかわかるようになるはず!
業務では設定の管理ページなどでよく使っています。

筆者オススメ技術書