経緯
- kaminariでやれば良いものをなんか意地はりました。
- Ruby on Rails 5 アプリケーションプログラミングを読んでいたのですが、limitメソッドとoffsetメソッドを使えばページネーションができると書いてあり、やってみようとしました。
目標形
- 数字リンク部分は常時3つ表示されるようにする。
(以下、例として総ページ数が5の場合。{}はアクティブな状態)
- 初回遷移時(1を押下した時)
- {1}[2][3][>][≫]
- 2を押下した時
- [<]{2}[3][4][>][≫]
- 3を押下した時
- [≪][<]{3}[4][5][>]
- 4を押下した時
- [≪][<][3]{4}[5][>]
- 5を押下した時
- [≪][<][3][4]{5}
実装
games_controller.rb
GAME_SIZE_PER_PAGE = 15
def index
@current = params[:page].nil? ? 1 : params[:page].to_i
@games = Game.all.limit(GAME_SIZE_PER_PAGE).offset(GAME_SIZE_PER_PAGE * (@current - 1))
@max_page = (Game.all.size.to_f / GAME_SIZE_PER_PAGE).ceil
@pagination = {}
@pagination['≪'] = 1 if @current >= 3
@pagination['<'] = @current - 1 if @current != 1
if @current + 2 >= @max_page
@pagination.merge!({@max_page - 2 => @max_page - 2, @max_page - 1 => @max_page - 1, @max_page => @max_page})
delete_list = [0, -1]
@pagination.delete_if do |key, value|
delete_list.include?(value)
end
else
@pagination.merge!({@current => @current, @current + 1 => @current + 1, @current + 2 => @current + 2})
end
@pagination['>'] = @current + 1 if @current != @max_page
@pagination['≫'] = @max_page if @current <= @max_page - 3
end
index.html.erb
※Bootstrap4の記述も入っている。
<ul class="pagination justify-content-center">
<% @pagination.each do |key, value| %>
<li class="page-item<%= ' active' if value == @current %>">
<%= link_to key, games_path(page: value), class: 'page-link' %>
</li>
<% end %>
</ul>
ポイント
- 総ページ数を求めるロジック
- 要素をページあたりの表示数で割って、総ページ数を求めるが、割ったあまりの分もページを用意する必要があるため、ceilメソッドを使って切り上げる。
@max_page = (Game.all.size.to_f / GAME_SIZE_PER_PAGE).ceil
- 0ページ目と-1ページ目が発生していたら削除
- 目標形でいう「4以降を押下した時」は、押下されたリンクを左端に置かずに、[3][4][5]が常に表示されるようにするため、このようなロジックを組んでいる。ただ、これだと0ページ目と-1ページ目が発生する場合がある(例えば総ページ数が1しかない場合など)ため、削除する処理を入れる。
@pagination.merge!({@max_page - 2 => @max_page - 2, @max_page - 1 => @max_page - 1, @max_page => @max_page})
delete_list = [0, -1]
@pagination.delete_if do |key, value|
delete_list.include?(value)
end
- pathヘルパーに引数を与えると、クエリパラメータになる
- どのリンクが押下されたかをクエリパラメータでコントローラーまで運ぶ。この書き方を知らなかったため、個人的には今回一番の学びポイントになった。
<% @pagination.each do |key, value| %>
<li class="page-item<%= ' active' if value == @current %>">
<%= link_to key, games_path(page: value), class: 'page-link' %>
</li>
<% end %>