Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Rails: TurboだけでStimulusやJSを書かずに無限オートスクロールする(翻訳)

概要

元サイトの許諾を得て翻訳・公開いたします。

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ファイルでデフォルトのrootPosts#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フレームワークを拡張し、最小限のコードでレスポンシブなアプリケーションを書けるようにします。

関連記事

Railsの技: “プログレッシブエンハンスメント”でHotwire的思考を身につける(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。