kaminariで"ページネーションのhtml要素"を非同期に出す
「kaminari 非同期」で検索すると、ページネーションの"リンク先のページ"を非同期で表示する方法が出てきます。kaminariのREADMEで紹介しているのもコレです。
https://github.com/kaminari/kaminari
そうではなくて、"ページネーションのhtml要素"を非同期で後から出す方法の紹介です。
検索してもあまり出てこなかったんですよね。
(↑ページネーションのhtml要素)
Gemのバージョンはkaminari (1.2.1)
, rails (6.0.3.4)
です。
なぜ非同期に?
普通にkaminariでページネーションの要素を出すときは、ビューで<%= paginate(@users) %>
のように書きます。
でもこれ、最後のページが何ページ目なのか求めるために、「レコードの全件数を取得するクエリ」が流れるんです。
SELECT COUNT(*) FROM users;
この場合は軽そうですが、色々と検索条件を付けていくと重いクエリになりそうです。なのでページネーションの要素は非同期で表示させたい、というのが動機です。
実装
最初に全体像を載せます。Railsで、Userモデルのindexページ、という想定です。
resources :users
def index
users = User.all.page(params[:page]).per(3)
respond_to do |format|
format.html { @users = users.without_count }
format.json { render :json => view_context.paginate(users).gsub('.json', '') }
end
end
<div id="paginator"></div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const path = location.pathname + '.json' + location.search;
const paginator = document.querySelector('#paginator');
fetch(path).then(response => response.text()).then(html => {
paginator.insertAdjacentHTML('afterbegin', html);
})
})
</script>
解説
処理の流れを順に説明します。最初、usersコントローラーのindexアクションにhtmlのリクエストが来たら、モデルに.without_count
メソッドを付けてからviewに渡します。
format.html { @users = users.without_count }
このメソッドをつけると、先程載せた「レコードの全件数を取得するクエリ」が発行されなくなります。
ページネーションの要素が付いてないhtmlが返され、同期処理は終わりです。
ここからが非同期です。返したhtml(内のjavascript)では、ページネーションの要素を非同期で要求します。
このときURLをlocation.pathname + '.json' + location.search;
として、
「URLのパスの末尾をjsonに変えただけのURL」を送ります。元のパスが/users?age=10
なら送るパスは/users.json?age=10
です。
送られてきたリクエストは、同じくusersコントローラーのindexアクションに送られます。
URLに.json
が付いているので、respond_to
はformat.json
の方が呼ばれます。
format.json { render :json => view_context.paginate(users).gsub('.json', '') }
view_context
はビューのインスタンスを返すメソッドです( https://apidock.com/rails/ActionView/Rendering/view_context )。 これを使うとビューのメソッドをコントローラー等から使うことができて、paginate(user)
が呼べます。
ここで使う変数user
にはwithout_count
は付けていませんので、「レコードの全件数を取得するクエリ」が発行されます。
paginate
メソッドで作られたページネーション要素ですが、formatがjsonだと(html以外だと)、リンクの末尾が.json
となってしまいます。ですのでgsubで消します。
出来上がったhtml文字列をjsonで包んで返します。
受け取ったjavascript側では、それを適当な要素にくっつければ完了です。
fetch(path).then(response => response.text()).then(html => {
paginator.insertAdjacentHTML('afterbegin', html);
})
非同期でページネーション要素を出せました。
(↓の例ではsleepを入れています)
いいですね、非同期。
この記事が誰かの役に立てればです。
Author And Source
この問題について(kaminariで"ページネーションのhtml要素"を非同期に出す), 我々は、より多くの情報をここで見つけました https://qiita.com/tetetratra/items/ff66bdfa4e4bb2758a5a著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .