3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Ruby on RailsAdvent Calendar 2022

Day 1

Hotwire勉強会ログ (1) Turbo

Last updated at Posted at 2022-11-30

はじめに

Rails 7から標準となったHotwireについて勉強会をしました。前半はTurboで後半がStimulusです。場所はSENQ六本木のスカイテラスで、晴れていたのですが少し寒かったかもしれません。

Railsで有名な万葉さんもHotwireをやっていくそうです。

私の場合は最近のRailsのJavaScriptの機能は使っていなくて、Turbolinksはオフにすることが多かったです。Rails 7ではHotwireが標準になってrails-ujsもフェードアウトしていきそうなので、ここは観念して勉強しようと思いました。

そしてForkwellさんのDHHへのインタービュー動画を見たことも影響しました。

というわけで、まずはHotwireのTurboをやっていきます。

Turboの概要

  • Turbo Drive
    • ページ全体を更新
    • Turbolinksと同じようなもの
  • Turbo Frames
    • ページの一部を更新
  • Turbo Streams
    • 複数箇所の更新が可能
  • Turbo Native
    • iOSのWKWebViewやAndroidのWebViewインスタンスを管理

以下の記事も参照してください。

Turbo Drive

  • Turbolinksの後継
  • 進行状況の表示
    • デフォルトは500ミリ秒
  • フォームの送信イベントの監視やレンダリングの中止などをJavaScriptで操作できる
    • これを使うことは少なそうだけど必要なこともあるのでしょうか
  • 特定のリンク先でTurboを無効
    • サードパーティのJavaScriptを使うときなど
    • これはTurbolinksでも使ったことがありました
  • フォーム送信後のリダイレクト
    • see_otherとかいろいろと注意するポイントがあるようです

  • リンクをキャッシュにプリロードする
    • <a href="/" data-turbo-preload>Home</a>でキャッシュさせることができて使うことがあるかも

Turbo Frames

  • <turbo-frame>要素を変更する
    • 1回の操作で変更できるのは1つの<turbo-frame>のみ
    • フレームに対する操作は置換のみ
  • <turbo-frame>には一意のIDが必要
  • Eager-LoadingとLazy-Loadingがある
  • 通常はフレームの操作はWebブラウザーの履歴には反映されないが、反映させることも可能

Turbo Streams

  • <turbo-stream>要素を変更する
    • 複数の<turbo-stream>を変更することができる
  • 7つのアクション
    • append / prepend / before / after / replace / update / remove
    • これらのアクションで対応できない時はStimulusコントローラーを使用する
  • WebSocketを利用してメールやチャットの受信のようにサーバーから起動することもできる

素振り

私がRailsでアプリを開発しはじめたときに読んでいたRails3レシピブック 190の技には項目にタグが付けられていて、素振り重要というタグが印象的でした。きっとTurboも素振り重要だと思うので、素振りをしていきたいと思います。

Hotwireの入門にはすばらしい教材があります。

今回の勉強会では時間が限られているため、Scaffoldで生成されるコードに最小限の変更でTurboを体験できるようにしました。アプリの操作手順によっては正しく動作しないので、操作をリセットする場合はhttp://localhost:3000/contentsにアクセスしてください。

% rails new sampleapp
% cd sampleapp
% rails g scaffold Content title:string body:text
% rake db:migrate

まずは適当にデータを3件ほど登録しておきます。

フォームの入力で表示条件を変更

Turbo Framesを使ってリストを更新するように変更します。

app/views/contents/index.html.erb
<%= form_with url: contents_path, method: :get, data: { turbo_frame: "contents-list" } do |f| %>
  <%= f.number_field :limit %>
  <%= f.submit "submit" %>
<% end %>

<%= turbo_frame_tag "contents-list" do %>
  <% @contents.each do |content| %>
    <%= render content %>
    <p>
      <%= link_to "Show this content", content %>
    </p>
  <% end %>
<% end %>
app/controller/contents_controller.rb
def index
  if params[:limit]
    @contents = Content.all.limit(params[:limit])
  else
    @contents = Content.all
  end
end

フォームに表示件数を入力してsubmitボタンをクリックすると、指定した表示件数を上限としたコンテンツが表示されます。このときGETメソッドを利用しているので通常はURLで引数が渡されますが、今回はTurbo Framesで処理しているためURLは変更されていません。

編集フォームをインラインで表示

それぞれのコンテンツをturbo_frame_tagで囲い、その中に編集リンクを追加します。

app/views/contents/index.html.erb
<%= turbo_frame_tag "contents-list" do %>
  <% @contents.each do |content| %>
    <%= turbo_frame_tag content do %>
      <%= render content %>
      <p>
        <%= link_to "Edit this content", edit_content_path(content) %>
        <%= link_to "Show this content", content %>
      </p>
    <% end %>
  <% end %>
<% end %>

edit.html.erbturbo_frame_tagで囲います。h1タグは削除します。

app/views/contents/edit.html.erb
<%= turbo_frame_tag @content do %>
  <%= render "form", content: @content %>

  <br>

  <div>
    <%= link_to "Show this content", @content %> |
    <%= link_to "Back to contents", contents_path %>
  </div>
<% end %>

コントローラーでupdateのリダイレクト先をcontents_urlにします。

app/controller/contents_controller.rb
def update
  respond_to do |format|
    if @content.update(content_params)
      format.html { redirect_to contents_url, notice: "Content was successfully updated." }
      format.json { render :show, status: :ok, location: @content }
    else
      format.html { render :edit, status: :unprocessable_entity }
      format.json { render json: @content.errors, status: :unprocessable_entity }
    end
  end
end

これでインラインの編集が可能になりました。

フラッシュメッセージの表示

Turbo Streamsを使ってフラッシュメッセージを表示するようにします。

app/controller/contents_controller.rb
def update
  if @content.update(content_params)
    flash.now.notice = "Content was successfully updated."
  else
    render :edit, status: :unprocessable_entity
  end
end
app/views/contents/update.turbo_stream.erb
<%= turbo_stream.replace @content %>
<%= turbo_stream.update "flash", partial: "flash" %>
app/views/contents/_flash.html.erb
<% if notice %>
  <div><%= notice %></div>
<% end %>

index.html.erbの上部にフラッシュメッセージを表示するようにします。

app/views/contents/index.html.erb
<div id="flash">
  <%= render "flash" %>
</div>

これでフラッシュメッセージが表示できましたが、コンテンツを更新するとEdit this contentShow this contentのリンクが消えます。このリンクを維持する場合は_content.html.erbにリンクを移動します。

おわりに

簡単な例ではありますが、HotwireのTurbo FramesとTurbo Streamsの動作を体験することができました。来年は既存のRailsアプリをRails 7に移行することがありそうで、Hotwireにも対応させていきたいと思います。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?