ページネーション 同期 非同期


はじめに

想定機能

  • ユーザー一覧(index)を表示するページの実装
  • ユーザーの表示数は3人で設定
  • 同期処理、非同期処理両方実装

環境

  • AWScloud9
  • Rails 5.2.2
  • ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-linux]
  • gem "kamianri"

実装 (同期処理)

前提

ユーザーの一覧ページはできているものとします。以下画像。

gem "kamianri" インストール

Gemfile
gem 'kaminari'
コマンド
bundle install

コントローラー修正

app/controllers/users_controller.rb
  def index
    @users = User.page(params[:page]).per(3).all
  end

ビュー修正

app/views/users/index.html.erb
<div id="pagenate">
  <p id="notice"><%= notice %></p>

  <h1>Users</h1>
  <h3>ユーザー数:<%= @users.size %></h3>

  <table class="table table-striped">
    <thead>
      <tr>
        <th>ID</th>
        <th>名前</th>
        <th>年齢</th>
        <th>性別</th>
        <th colspan="3"></th>
      </tr>
    </thead>

    <tbody>
      <% @users.each do |user| %>
        <tr>
          <td><%= user.id %></td>
          <td><%= user.name %></td>
          <td><%= user.age %></td>
          <td><%= user.sex %></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 %>
  <br>
</div>
<%= link_to 'New User', new_user_path %>

同期処理完成

  • 同期処理でのページネーションはめっちゃ簡単ですね。gemの力は絶大です。ですが、頼りすぎは禁物ですね。

実装(非同期処理)

コントローラー修正

app/controllers/users_controller.rb
  def index
    @users = User.page(params[:page]).per(3).all

    respond_to do |format|
      format.html
      format.js
    end

  end
  • コントローラーのindexアクションで最後にformat.jsが評価され、jsフォーマットが返される。そして、index.js.erbを探しに行きます

JSビュー作成

app/views/users/index.js.erb
  $('#pagenate').html("<%= escape_javascript(render 'index_page') %>");
  • index.js.erbでは、htmlメソッドで<%=render 'index_page'%>をidがpagenateのdiv要素にレタリングされます。
  • 次にパーシャルの_index_page.html.erbを作成します。

パーシャル作成

app/views/users/_index_page.html.erb
  <p id="notice"><%= notice %></p>

  <h1>Users</h1>
  <h3>ユーザー数:<%= @users.size %></h3>

  <table class="table table-striped">
    <thead>
      <tr>
        <th>ID</th>
        <th>名前</th>
        <th>年齢</th>
        <th>性別</th>
        <th colspan="3"></th>
      </tr>
    </thead>

    <tbody>
      <% @users.each do |user| %>
        <tr>
          <td><%= user.id %></td>
          <td><%= user.name %></td>
          <td><%= user.age %></td>
          <td><%= user.sex %></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, remote: true %>
  <br>

index.html.erb修正

app/views/users/index.html.erb
上記省略

 </table>
##以下のerbに`remote: true`を追記
  <%= paginate @users, remote: true %>
  <br>
</div>
<%= link_to 'New User', new_user_path %>
  • <%= paginate @users, remote: true %>とすることで非同期で処理をするという命令になります。

完成!

解説

respond_to do |format|~end

  • indexアクションのrespond_to 〜ブロック〜では、指定されたフォーマット形式で@userの情報を返しています。例えば以下のように、indexアクションを修正すると、/users.jsonにアクセスすると以下のような情報が得られます。
app/controllers/users_controller.rb
  def index
    @users = User.all.page(params[:page]).per(3)

    respond_to do |format|
      format.html
      format.js
      format.json { render :json => @users }
      format.xml  { render :xml => @users }
    end
  end


- /users.xmlにアクセスすると以下のようにXML形式の@usersの情報が得られます。

- 同じようにhtml、jsも情報を送っている。jsに限っては,アクセスしてもエラーになってしまいます。
- 作成した機能を例にとれば、実際の非同期の流れは、
index.html.erb→ indexアクション→index.js.erb→_index.page.html.erbという感じになります。

index.js.erb

app/views/users/index.js.erb
  $('#pagenate').html("<%= escape_javascript(render 'index_page') %>");
  • 上のjsコードはidがpagenateの要素に、htmlメソッドを使って、 <%= render 'index_page' %>を挿入するという意味です。
  • htmlメソッドは、任意の要素に指定した、htmlを挿入するjavascriptのメソッドです。
  • escape_javascriptは、javascriptのエスケープメソッドですね。jsコードの中では、erb文は直接かけないので、こういう書き方になります。

まとめ

少し長くなりましたが、ほんとに簡単にできました。
さらに便利なkaminariメソッド群は公式へ→https://github.com/kaminari/kaminari