Rails: TurboだけでStimulusやJSを書かずに無限オートスクロールする(翻訳)
本記事では、Rails 7アプリケーションを作成して無限オートスクロールを実装します。この機能はHotwireのTurboだけで実現されており、JavaScriptコードは1行も書く必要がありません。
このアプリはデフォルトで10件の投稿を表示します。ページ末尾に到達すると、投稿がなくなるまで10件ずつ投稿を追加表示します。
Rails 7アプリケーションを作成する
最初にRails 7アプリケーションを作成します。
rails _7.0.3_ new infinite-scroll-turbo
モデルを追加する
話を簡単にするため、body
属性だけを持つシンプルなモデルを追加します。
rails g scaffold Post body:text
rails db:migrate
PostsページをHomeページに設定する
config\routes.rbファイルでデフォルトのroot
をPosts#index
に変更します。
root "posts#index"
SimpleCSSを追加する
SimpleCSSを追加して外観を整えます。
<link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css">
Turboのフォームを追加して投稿を数件作成する
scaffoldコマンドで作成したPostフォームのパーシャルをindexページに追加します。
<div id="new_post">
<%= render partial: "posts/form", locals: { post: Post.new } %>
</div>
これで投稿を追加しやすくなりました。
Turbo Frameを追加して投稿をTurbo Streamでリスト表示する
app/views/posts/index.html.erbファイルに以下のturbo_frame
を追加します。
<div id="posts">
<%= turbo_frame_tag "posts", src: posts_path(format: :turbo_stream), loading: :lazy %>
</div>
app/views/posts/index.turbo_stream.erbファイルを作成して、上に対応するTurbo用のindexコードを追加します。
<%= turbo_stream.append "posts" do %>
<%= render partial: "posts/post", collection: @posts %>
<% end %>
なお、index.turbo_stream.erbを使わなくてもturbo_stream
メソッドにrespond_to
のフォーマットを追加すれば同じことができます。
Posts
のTurbo FramesとPosts#index
のTurbo Streamsには、TurboとRails 7のマジックが現れています。JavaScriptコードは1行も書いていませんが、DOMがPosts
にさしかかるとturbo_frame
が動作するので、Posts#index
ページから動的に読み込まれます。
ページネーションを追加する
読み込みを機能させるために、ページネーション機能を実装する必要があります。コントローラのPosts#index
を以下のように変更します。
def index
page_limit = 10
@current_page = params[:page].to_i
@posts = Post.offset(page_limit*@current_page).limit(page_limit)
@next_page = @current_page + 1 if(Post.all.count > page_limit*@current_page + page_limit)
end
上のコードは、10件ずつの投稿グループのparams[:page]
に基づいて@posts
を返します。件数はpage_limit
で設定できます。
また、次のページがあるかどうかを示す@next_page
変数と現在のページを表す@current_page
変数も設定します。
Turbo Framesで投稿を動的に読み込む
上のセクションで説明したように、Turbo Frameを使うとTurbo Stream経由でget
メソッドを呼び出せます。この方法を使って、投稿がなくなるまでフレームにページを追加します。
app/views/posts/index.html.erbのコードを以下のように書き換えます。
<div id="posts">
</div>
<%= turbo_frame_tag "load_more", src: posts_path(format: :turbo_stream), loading: :lazy %>
app/views/posts/index.turbo_stream.erbも以下のように書き換えます。
<%= turbo_stream.append "posts" do %>
<%= render partial: "posts/post", collection: @posts %>
<% end %>
<% if @next_page.present? %>
<%= turbo_stream.replace "load_more" do %>
<%= turbo_frame_tag "load_more", src: posts_path(page: @next_page, format: :turbo_stream), loading: :lazy %>
<% end %>
<% end %>
ここには、空のdiv#posts
と、投稿がそれ以上なくなるまで投稿を再帰的に遅延読み込みするフレームがあります。
turbo_frame_tag#load_more
の末尾に到達するとindex.turbo_stream
を呼び出し、ページネーションされた投稿をdiv#posts
に追加し、div#load_more
を@next_page
の新しいturbo_frame_tag
に置き換えます。
まとめ
TurboはRailsフレームワークを拡張し、最小限のコードでレスポンシブなアプリケーションを書けるようにします。
概要
元サイトの許諾を得て翻訳・公開いたします。