[Rails]テーブルにページネーションを付ける[初心者向け]

以下のようなページネーションを実装する

![ページネーション.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/792137/de8cee1c-493c-0558-efd3-6aa62167e3d3.png)

## 注意
画像に表示されているメールアドレスは実在のものではありません
RailsにBootstrapが適用されていること
> 参考：[Railsアプリで Bootstrap 4 を利用する - Qiita](https://qiita.com/NaokiIshimura/items/c8db09daefff5c11dadf)

## 環境
- rails：5.1.6
- ruby：2.6.3p62

## 初期コード
コントローラ

```rb:app/controllers/users_controller.rb
### 以上略 ###
def index
  @users = User.all
end
### 以下略 ###
```

ビュー

```erb:app/views/users/index.html.erb
<div class="mt-2 mb-2 mr-5 ml-5 pr-5 pl-5">
  <table class="table table-sm table-hover">
    <thead class="thead-dark">
      <tr>
        <th>Name</th>
        <th>Email</th>
        <th></th>
        <th></th>
        <th></th>
      </tr>
    </thead>
  
    <tbody>
      <% @users.each do |user| %>
        <tr>
          <td><%= user.name %></td>
          <td><%= user.email %></td>
          <td><%= link_to 'Show', user %></td>
          <td><%= link_to 'Edit', edit_user_path(user) %></td>
          <td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td>
        </tr>
      <% end %>
    </tbody>
  </table>
</div>
```

## 初期画面と実現したい画面
| 初期画面 |
| ---- |
Userが全て表示されている
![初期画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/792137/8f4fc397-c0dd-6c9e-383c-3e453dbaa146.png)

| 実現したい画面（1ページ目） |
| ---- |
20件ずつ表示する
![ページネーション_1st.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/792137/879fb4c5-4390-4e50-ed43-869627401357.png)

| 実現したい画面（2ページ目） |
| ---- |
全21件なので2ページ目は1件のみ
![ページネーション_2nd.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/792137/254e81ee-3f42-925c-2594-6b5ab88537b9.png)

## ページネーション機能実装
### 実装部分
ページネーションを実現するには、kaminariを使う
以下を追記

```rb:Gemfile
gem 'kaminari'
gem 'kaminari-bootstrap'
```

以下でgemをインストール

```bash:console
bundle install
```

**コントローラー**
`per(num)`：numは何件ごと表示するか（今回は20件ごと）
以下の差分を参考に変更

```diff:app/controllers/users_controller.rb
 def index
-  @users = User.all
+  @users = User.page(params[:page]).per(20)
 end
```

**ビュー**
`paginate`を書いたところにページネーションが表示される（今回はテーブルの後ろにする）

```diff:app/views/users/index.html.erb
 <div class="mt-2 mb-2 mr-5 ml-5 pr-5 pl-5">
   <table class="table table-sm table-hover">
     <thead class="thead-dark">
       <tr>
         <th>Name</th>
         <th>Email</th>
         <th></th>
         <th></th>
         <th></th>
       </tr>
     </thead>

     <tbody>
       <% @users.each do |user| %>
         <tr>
           <td><%= user.name %></td>
           <td><%= user.email %></td>
           <td><%= link_to 'Show', user %></td>
           <td><%= link_to 'Edit', edit_user_path(user) %></td>
           <td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td>
         </tr>
       <% end %>
     </tbody>
   </table>
+  <%= paginate @users %>
 </div>
```

| 画面（一部省略） |
| ---- |
![ページネーション01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/792137/78361934-47e7-7270-565c-31314eceaa14.png)

ページネーションが実現できているが、見た目がダサいし使いづらい

### 見た目の変更
以下でkaminariをカスタマイズするファイル作成

```bash:console
rails g kaminari:views default
```

以下が表示されていれば、`app/views`に`kaminari`フォルダが生成されている

```bash:結果
      create  app/views/kaminari/_paginator.html.erb
      create  app/views/kaminari/_prev_page.html.erb
      create  app/views/kaminari/_page.html.erb
      create  app/views/kaminari/_last_page.html.erb
      create  app/views/kaminari/_gap.html.erb
      create  app/views/kaminari/_first_page.html.erb
      create  app/views/kaminari/_next_page.html.erb
```

`app/views/kaminari`に生成されたファイルを編集する（主にpage-linkクラスを適用する）

```erb:app/views/kaminari/_first_page.html.erb
<span class="first">
  <%= link_to_unless current_page.first?, t('views.pagination.first').html_safe, url, :remote => remote, class: 'page-link' %>
</span>
```

```erb:app/views/kaminari/_last_page.html.erb
<span class="last">
  <%= link_to_unless current_page.last?, t('views.pagination.last').html_safe, url, :remote => remote, class: 'page-link' %>
</span>
```

```erb:app/views/kaminari/_next_page.html.erb
<span class="next">
  <%= link_to_unless current_page.last?, t('views.pagination.next').html_safe, url, :rel => 'next', :remote => remote, class: 'page-link' %>
</span>
```

```erb:app/views/kaminari/_page.html.erb
<% if page.current? %>
  <span class="page-item disabled">
    <%= link_to page, url, {class: 'page-link'} %>
  </span>
<% else %>
  <span class="page-item">
    <%= link_to_unless page.current?, page, url, {:remote => remote, class: 'page-link', :rel => page.next? ? 'next' : page.prev? ? 'prev' : nil} %>
  </span>
<% end %>
```

```erb:app/views/kaminari/_prev_page.html.erb
<span class="prev">
  <%= link_to_unless current_page.first?, t('views.pagination.previous').html_safe, url, :rel => 'prev', :remote => remote, class: 'page-link' %>
</span>
```

ついでに、ページネーションを中央寄せにする

```scss:app/assets/stylesheets/application.scss
.pagination {
  justify-content: center;
}

```

| 画面（一部省略）|
| ---- |
![ページネーションpage-link_中央寄せ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/792137/e4ee8517-fbf8-3e15-6ea2-f829587ba1be.png)

ページネーション機能完了
NextやLastなどの日本語化方法は省略します

### 参考
> [【rails】kaminariを使ってページネーションを作る - Qiita](https://qiita.com/you8/items/df68aaee3010e282d1ae)
[【Rails】kaminariの使い方をマスターしよう！ - Pikawaka](https://pikawaka.com/rails/kaminari)
[Kaminariの使い方 まとめ - Hatena Blog](http://nekorails.hatenablog.com/entry/2018/10/15/005146)

## おまけ
追加用件として以下が考えられる

- 1ページあたりの表示件数を20件と決め打ちしたが、利用者が表示件数を切り替えられるようにしたい
- 全部で何件あって、そのうち何件ページネーションで表示されているのか知りたい

| イメージ（一部省略） |
| ---- |
![完成イメージ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/792137/a5d78cec-5673-b4c0-a784-913b77b6f728.png)


### 利用者が表示件数を切り替えられる機能
コントローラー

```diff:app/controllers/users_controller.rb
 def index
-  @users = User.page(params[:page]).per(20)
+  @users = User.page(params[:page]).per(per_page)
 end

 ### 以下一部省略 ###

 private
+  def per_page
+    # 今回は5, 10, 20, 50, 100件で利用者が切り替えられるようにする（デフォルトは20）
+    %W[5 10 20 50 100].include?(params[:per]) ? params[:per] : 20
+  end
```

ビュー

```diff:app/views/users/index.html.erb
 <div class="mt-2 mb-2 mr-5 ml-5 pr-5 pl-5">
+  <div class="float-right">
+    表示件数：
+    <% [5, 10, 20, 50, 100].each do |per| %>
+      <% unless @users.limit_value == per %>
+        <%= link_to per, users_path(per: per), {class: 'mr-2'} %>
+      <% else %>
+        <a class="mr-2"><%= per %></a>
+      <% end %>
+    <% end %>
+  </div>
   <table class="table table-sm table-hover">
     <thead class="thead-dark">
       <tr>
         <th>Name</th>
         <th>Email</th>
         <th></th>
         <th></th>
         <th></th>
       </tr>
     </thead>
     <tbody>
       <% @users.each do |user| %>
 ### 以下略 ###
```

| 表示件数切り替え画面（一部省略） |
| ---- |
今回はテーブルの上に表示した
場所はお好みで

![表示件数切り替え機能.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/792137/728e6ce6-e904-8ec7-ac6e-9e8c58456e5b.png)

### 全件数と表示件数表示
ビュー

```diff:app/views/users/index.html.erb
 <div class="mt-2 mb-2 mr-5 ml-5 pr-5 pl-5">
+  <div class="float-left">
+    <%= "#{@users.count}件 / #{@users.total_count}件" %>
+  </div>
   <div class="float-right">
     表示件数：
     <% [5, 10, 20, 50, 100].each do |per| %>
       <% unless @users.limit_value == per %>
         <%= link_to per, users_path(per: per), {class: 'mr-2'} %>
       <% else %>
         <a class="mr-2"><%= per %></a>
       <% end %>
     <% end %>
   </div>
 ### 以下略 ###
```

|全件数と表示件数表示画面|
| ---- |
1ページあたりの表示件数を20件としているが、最後のページは1件のみなので正しく表示されている（左上）

![全件と件数表示画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/792137/5a6b7293-c735-8ef3-4f4d-1f958a272a60.png)