[Rails6]モーダルの中で非同期ページネーションできるようにする
こんにちは、だむはです。
「sister(シスター)」というサービスを個人開発しています。
先週、通知一覧の機能を追加したのですが、モーダルの中のページネーションに苦戦したので、メモとして記事にしてみます。
通知一覧出来上がりはこれ
1、モーダルで通知一覧
2、モーダル内にページネーション
2、非同期でページネーション
要件
- Rails6
- BootStrap(モーダル)
- kaminari(ページネーション)
※BootStrapとkaminariの導入説明は省きます
実装方法
1、Controllerにデータ取得処理を書く
今回は、モーダル内でのページネーションについてなので、Controllerでのデータ取得条件の説明は省きます。
def index
#通知データを取得
@notifications = current_user.passive_notifications.page(params[:page])
.per(20)
.order(created_at: :desc)
#通知一覧に遷移したら、既読済みにする
@notifications.where(checked: false).each do |notification|
notification.update_attributes(checked: true)
end
if params[:page].present?
render 'pagenate.js.erb' #2ページ目はこっち
else
render 'index.js.erb' #1ページ目はこっち
end
end
2、通知一覧をモーダル表示させる
通知一覧呼び出しをAjax化(非同期)する
今回は通知一覧はヘッダーのにおくので、applications.html.erbに配置します。
<%= link_to '/notifications', remote: true %>
remote: trueをつけると、リクエストがhtmlではなくjsになります。
index.html.erbではなくindex.js.erbが読み込まれるようになります。
index.js.erbを作成する
$("#notify-modal").html("<%= escape_javascript(render 'index', locals: { notifications: @notifications } ) %>")
$("#notify-modal").modal("show")
1行目:applications.html.erbのid=notify-modalに対して、_index.html.erbをレンダリングします。
2行目:モーダルを表示する処理です。
表示用のdivを配置する
モーダルの表示はBootStrapを使用します。
applications.html.erbにモーダル表示用のdivを配置します。
index.js.erbで記載した「#notify-modal」の部分です。
<div id="notify-modal" class="modal notify-modal fade" tabindex="-1" role="dialog" aria-hidden="true"></div>
場所はどこでも大丈夫です。
モーダル表示用パーシャル_index.html.erbを作成する
以下のようにmodal-dialogとmodal-contentをクラス名にして表示したい内容をdivで囲ってあげます。
今回は、通知の表示部分はパーシャルを作成し、レンダーするようにしています。
<div class="modal-dialog modal-lg" style="z-index: 9999" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="Modal">通知</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="閉じる">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<% if @notifications.exists? %>
<%= render partial: 'list', locals: { nitifications: @nitifications } %>
<% else %>
<p>通知はありません</p>
<% end %>
</div>
</div>
</div>
これで、モーダル表示は完成です。
通知一覧用パーシャル_list.html.erbを作成する
ページネーションを非同期で実施するため、ここでさらにパーシャルを作成し、レンダーします。
理由はモーダル画面と共通のパーシャルを使用すると、モーダルのなかにモーダルが表示されるような処理になってしまうためです。
以下の方法で、通知部分のみが非同期でレンダーし、非同期ページネーションがうまくいくようになります。
<%= render @notifications %>
<%= paginate @notifications, remote: true %>
通知詳細用パーシャル_notification.html.erbを作成する
通知用のパーシャルを作成します。今回はブログが作成された際のみ通知される仕様とします。
<% if notification.action == "blog" %>
<span>\ お知らせ / <%= link_to "#{notification.blog.title}", notification.blog.url, target: :_blank, class: "stretched-link" %></span>
<% end %>
ここまでで、一旦、ページネーションなしのデータ表示は完成です。
ここから、モーダル内で非同期ページネーションするための処理を書いていきます。
3、モーダル内で非同期ページネーションさせる
1ページ目と2ページ目以降のレンダリング先を変更する
2ページ目以降に遷移するときは、モーダル表示してほしくありません。
そのため、冒頭でも記載している通り、Controller側でparams[:page]に値が入っている場合(ページネーションされている場合)はモーダル表示されないようにする処理を入れてあげます。
def index
#通知データを取得
@notifications = current_user.passive_notifications.page(params[:page])
.per(20)
.order(created_at: :desc)
#通知一覧に遷移したら、既読済みにする
@notifications.where(checked: false).each do |notification|
notification.update_attributes(checked: true)
end
if params[:page].present?
render 'pagenate.js.erb' #2ページ目以降はこっち!!!!
else
render 'index.js.erb'
end
end
これで、params[:page]に値が入っているときはpagenate.js.erbにレンダリングされます。
pagenate.js.erbを作成する
$("#pagenate").html("<%= escape_javascript(render 'list', locals: { notifications: @notifications } ) %>")
1行目:_index.html.erbのid=pagenateに対して、_list.html.erbをレンダリングします。
_list.html.erbにレンダリング
最初のモーダル表示の際と違い、直接_list.html.erbにレンダリングすることで、モーダル表示されることを防ぎます。
<%= render @notifications %>
<%= paginate @notifications, remote: true %>
_index.html.erbに
_index.html.erbはすでにモーダル表示されているパーシャルです。
_index.html.erbのclass="modal-body"の部分にid=pagenateを追加します。
pagenate.js.erbで記載した「#pagenate」の部分です。
<div class="modal-dialog modal-lg" style="z-index: 9999" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="Modal">通知</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="閉じる">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body" id="pagenate"> ←ここ!!!!
<% if @notifications.exists? %>
<%= render partial: 'list', locals: { nitifications: @nitifications } %>
<% else %>
<p>通知はありません</p>
<% end %>
</div>
</div>
</div>
これで、モーダル内の非同期ページネーションができるようになります。
まとめ
モーダル内でのページネーション方法がなかなか見つからなかったので、自分で試行錯誤して実装してみました。もっといい方法があったら誰か教えてください!
Discussion