Help us understand the problem. What is going on with this article?

【Rails】 DataTables 実装方法

はじめに

RailsアプリケーションでDataTablesを使っている記事を見かけなかったので、まとめることにしました。
やり方がわかっていないと細かい仕様を変更するのに時間がかかってしまいますが、やり方さえ覚えてしまえばとても使いやすい強力なツールです。
Bootstrap や jQuery UI を使うことによって、時間をかけずに多機能&良いレイアウトを提供してくれるため、爆速で開発できるRailsととても相性がいいと個人的には思っています。

関連リンク

関連リンクを下記に載せておくので、必要であれば参考にしてください。。

DataTables

DataTables は、HTMLのテーブルに、ページ切り替え、ページ当たりの件数設定、ソート、フィルタなどの機能を簡単に追加できるjQuery プラグインのライブラリ

使い方

設定

GemFile
# ページネーションにはkaminariを使用するため
gem 'kaminari'
gem 'jquery-datatables-rails'
$ bundle install
$ rails g jquery:datatables:install
$ rails g jquery:datatables:install bootstrap3
app/assets/javascripts/application.js
//= require dataTables/jquery.dataTables
//= require dataTables/bootstrap/3/jquery.dataTables.bootstrap
app/assets/stylesheets/application.css
*= require dataTables/jquery.dataTables
*= require dataTables/bootstrap/3/jquery.dataTables.bootstrap

※ version3系をダウンロードする。(GitHubにBootstrap4の記載がないため念のため。。)
4系でも使えると知っている方がいればコメントください。。

# 下記のようにファイルを配置する
app/assets/javascripts/bootstrap.min.js
app/assets/stylesheets/bootstrap.min.css

アプリケーション実装

前提条件

Userモデルを scaffold で作成していること。

全体像

厳密には異なるところもありますが、ざっくりと全体像を示すとこんな感じになります。

① id が users の要素を持った table を作成(table#users)し、CoffeeScript にて読み込み
② ajax で CoffeeScript から Rails 側へ、データの算出を要求する
③ コントローラーにて ajax を受け取り、 UserDatatable モデルへデータの算出を要求する
④ 要求を満たすデータを返す
⑤ ④のデータを json ファイルにして返す
⑥ CoffeeScript にて実装したテーブルを HTML にて表示する

image.png

ビュー

slim だと本当にシンプルに書くことができます。

app/views/users/index.html.slim
// 検索項目
button#search_btn type="button"
  |詳細検索
button#search_exec_btn type="button"
  |検索
button#search_clear_btn type="button"
  |クリア
table
  tr
    td = label :user, :id
    td = text_field_tag :id, '', id: "search_id"
    td = label :user, :username
    td = text_field_tag :username, '', id: "search_username"
    td = label :user, :name
    td = text_field_tag :name, '', id: "search_name"

// DataTables のテーブル表示
table#incidents

ルーティング

jsonファイルを dataTables 側に渡すときに使用するルーティング。
のちほど、Javascriptにて使用する。

config/routes.rb
resources :users do
  collection do
    post 'ajax_data'
  end
end

コントローラー

UserDatatableのインスタンスをjsonファイルとして返す。

app/controllers/users_controller.rb
def ajax_data
  respond_to do |format|
    format.html
    format.json {render json: UsersDatatable.new(params) }
  end
end

UserDatatableはパラメーターを受け取って、SQL を実行し JSON 形式に変換するクラス。
テーブルに表示させたい項目や検索結果などをこちらのファイルにて割り出している。

app/datatables/users_datatable.rb
class UsersDatatable
  attr_accessor :params

  def initialize(params)
    @params = params
  end

  # jQuery DataTables へ渡すためのハッシュを作る
  # 補足:コントローラーの render json: で指定したオブジェクトに対して as_json が呼び出される
  def as_json(options = {})
    {
        recordsTotal: User.count, # 取得件数
        recordsFiltered: users.total_count, # フィルター前の全件数
        data: users, # 表データ
    }
  end

  def users
    @users ||= fetch_users
  end

  # 検索条件や件数を指定してデータを取得
  def fetch_users
    User.where(search_sql).page(page).per(per)
  end

  # カラム情報を配列にする
  def columns
    return [] if params["columns"].blank?
    params["columns"].to_unsafe_h.map{|_,v| v["data"]}
  end

  # 検索ワードが指定されたとき
  def search_sql
    search_sql = []
    for column, search_params in params["columns"] do
      search_sql.push("#{search_params["data"]} like '%#{search_params["search"]["value"]}%'") if search_params["search"]["value"].present?
    end
    search_sql.join(" and ")
  end

  # ソート順
  def order_sql
    return "" if params["order"]["0"].blank?
    order_data = params["order"]["0"]
    order_column = columns[order_data["column"].to_i]
    # "id desc" のようにSQLの一部を作る
    "#{order_column} #{order_data["dir"]}"
  end

  # kaminari 向け、ページ数
  def page
    params["start"].to_i / per + 1
  end

  # kaminari 向け、1ページで取得する件数
  def per
    params["length"].to_i > 0 ? params["length"].to_i : 10
  end
end

Javascript

app/assets/javascripts/users.coffee
$ ->
  # DataTables オブジェクト作成(ここではhtmlのid=users)
  user_table = new DataTables($('#users'))
  # ajax にて、 json ファイルを読み込み。作成したルーティングを引数に入れる。
  user_table.setAjax("/users/ajax_data")
  # user_table へカラムを追加する
  user_table.setColumns([
    { data: 'id',         title: 'ユーザID', width: '5%' },
    { data: 'username',   title: 'ユーザ名', width: '25%' },
    { data: 'name',       title: '名前',    width: '30%' },
    { data: 'created_at', title: '登録日時', width: '20%' },
    { data: 'updated_at', title: '更新日時', width: '20%' },
  ])
  user_table.setOption(false, true, true)
  # 表示順番を0番目のカラムを昇順で表示(ここではidを昇順で表示)
  user_table.setOrders([[0,'asc']])
  # #users へ user_table を描写する。
  user_table.drawTable()

  # 各行をクリックすると詳細画面へ遷移するように設定
  $('#users tbody').on 'click', 'tr', ->
    data = $('#users').dataTable().fnGetData(this);
    document.location = "/users/#{data.id}"

  # 詳細検索ボタンを押すと、検索ボタンおよびカラムが表示されるように設定
  $('#search_btn').on 'click', ->
    $('.search-body').slideToggle(200)
    $('#search_exec_btn').slideToggle(200)
    $('#search_clear_btn').slideToggle(200)

  # 検索ボタンを押すと、表示されているテーブルのカラムが検索条件に合致したものだけを表示するように設定。
  $('#search_exec_btn').on 'click', ->
    # dataTable の API を利用する。
    user_table_api = $('#users').dataTable().api()
    # 各検索カラムの値を読み込み
    search_id =       $('#search_id').val()
    search_username = $('#search_username').val()
    search_name =     $('#search_name').val()
    # 検索結果をテーブルに描画する
    user_table_api.columns(0).search(search_id).draw()
    user_table_api.columns(1).search(search_username).draw()
    user_table_api.columns(2).search(search_name).draw()

  $('#search_clear_btn').on 'click', ->
    # 検索欄 入力内容 初期化
    tmp_array = ["id", "username", "name"]
    tmp_array.forEach (key) ->
      $("#search_#{key}").val('').trigger('change')

まとめ

少し複雑な実装でしたので、Keynoteで作った全体像を用いての説明でした。
いかがでしたでしょうか。少しでも理解の助けになれればなと思います。

また、DataTables は高機能なライブラリですので、他にもできることを紹介していきたいと思います。

参考

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした