Ruby
Rails
rubygems
RubyOnRails

[Rails]will_paginateで表示件数を選択できるページネーションを実装する

Railsでデータ表を表示する際、行数(データ数)が多い場合には当然ページが無駄に長くなってしまうため、ページ分割を行った方が良い。
ここではページネーションが簡単に実装できるGem「will_paginate」を紹介して、+αとしてドロップボタンによる表示件数検索も実装する。

ページネーション

ページ割りのこと。一番馴染み深い例で言えば、Google検索のコレ。
スクリーンショット 2018-07-17 23.56.42.png
ページにデータ表を作る際にn行ずつ表示させることで、よりスッキリしたデザインにできる。

will_paginateによる実装

book_list.html.erbに、こんな感じの表を作成したとする。

スクリーンショット 2018-07-18 0.11.25.png

12件一気に表示すると縦に長くなってしまうため、まずは5件ずつ表示するようにページネーションを実装する。

※参考
http://blog.otsukasatoshi.com/entry/2017/07/10/120000

まずはGemfileにwill_paginateを追加する

gem 'will_paginate'
gem 'bootstrap-will_paginate'

will_paginateだけでもページネーションは実装できるが、見た目が圧倒的に良くなるのでbootstrap-will_paginateも追加する事を推奨する。

次に、Controllerを以下のように修正

book_controller.rb
def book_list
   @books = Book.paginate(page: params[:page], per_page: 5)
end

will_paginateが導入されている時点で、各Modelにpaginateメソッドが追加される。
表示するデータがBookの全データだった場合は、.allが.paginateに置き換わるだけ。
page:は参照しているページ番号の事で、これは自動的に設定される値なのでそのままでOK。
per_page:は1ページに表示する件数。今回は5件ごとに表示するので5。

最後にViewに以下の2行を追加。

book_list.html.erb
<tbody class="text-center">
 <%= will_paginate @books, :previous_label => ' &lt 前へ', :next_label => '次へ &gt' %> # この行と
  <% @books.each do |book| %>
   <tr>
    <td><%= book.id %></td>
    <td><%= book.title %></td>
     [...省略...]
   </tr>
  <% end %>
</tbody>
<%= will_paginate @books, :previous_label => ' &lt 前へ', :next_label => '次へ &gt' %> # この行を追記

これだけでページネーションの実装が完了する。will_paginateすごい。
Viewで追加する行は

<%= will_paginate @books %>

だけで十分であるが、defaultだと「前へ」「後ろへ」の表記が英語になってしまうため、上のコードでは少しいじってある。

実際に動かしてみたモノがこちら。
スクリーンショット 2018-07-18 0.30.31.png

Viewで挿入した位置に、ちゃんとボタンが追加されている。bootstrap-will_paginateのおかげで見た目も悪くない。

表示件数選択機能

上の例では5件ずつ表示したが、表示件数も弄れるようにした方が快適なので、ドロップダウンから設定できるようにする。
will_paginateのREADMEを見たところ、そういうドロップダウンボタンが用意されてる訳ではないようなので、自分で作成する。

ここでは"/book_list/:件数"に飛ばして、paramsからper_pageを設定することとする。
ただし、:pageはpaginateで予約されているため、パラメータ名は別の単語を使う(ここでは:perとする)

route.rb
get "book_list/:per" => "book#book_list_page"
get "book_list" => "book#book_list"
book_controller.rb
def book_list
   @page = 5
   @books = Book.paginate(page: params[:page], per_page: @page)
end

def book_list_page
   @page = params[:per]
   @books = Book.paginate(page: params[:page], per_page: @page)
   render("book_list")
end
book_list.html.erb
<div class="dropdown">
 <button class="btn btn-default dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
  <%= @page %>
  <span class="caret"></span>
 </button>
 <ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
  <li><%= link_to("3", "/book_list/3") %></li>
  <li><%= link_to("5", "/book_list/5") %></li>
  <li><%= link_to("10", "/book_list/10") %></li>
  <li><%= link_to("20", "/book_list/20") %></li>
 </ul>
 <label>件ずつ表示する</label>
</div>

<thead class="text-center">
[...省略...]
<tbody class="text-center">
 <%= will_paginate @books, :previous_label => ' &lt 前へ', :next_label => '次へ &gt' %>
  <% @books.each do |book| %>
   <tr>
    <td><%= book.id %></td>
    <td><%= book.title %></td>
     [...省略...]
   </tr>
  <% end %>
</tbody>
<%= will_paginate @books, :previous_label => ' &lt 前へ', :next_label => '次へ &gt' %>
[以下略]

これを実装した結果がこちら。
スクリーンショット 2018-07-18 0.52.20.png
左上のドロップダウンから表示件数が選択でき、表示件数を変えるたびにページ数も変化する。

まとめ

will_paginateで簡単にページネーションが実装できた。
表示件数を設定するボタンも実装することができた。