はじめに
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
インスタンスを管理
- iOSの
以下の記事も参照してください。
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>
のみ - フレームに対する操作は置換のみ
- 1回の操作で変更できるのは1つの
-
<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を使ってリストを更新するように変更します。
<%= 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 %>
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
で囲い、その中に編集リンクを追加します。
<%= 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.erb
をturbo_frame_tag
で囲います。h1
タグは削除します。
<%= 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
にします。
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を使ってフラッシュメッセージを表示するようにします。
def update
if @content.update(content_params)
flash.now.notice = "Content was successfully updated."
else
render :edit, status: :unprocessable_entity
end
end
<%= turbo_stream.replace @content %>
<%= turbo_stream.update "flash", partial: "flash" %>
<% if notice %>
<div><%= notice %></div>
<% end %>
index.html.erb
の上部にフラッシュメッセージを表示するようにします。
<div id="flash">
<%= render "flash" %>
</div>
これでフラッシュメッセージが表示できましたが、コンテンツを更新するとEdit this content
とShow this content
のリンクが消えます。このリンクを維持する場合は_content.html.erb
にリンクを移動します。
おわりに
簡単な例ではありますが、HotwireのTurbo FramesとTurbo Streamsの動作を体験することができました。来年は既存のRailsアプリをRails 7に移行することがありそうで、Hotwireにも対応させていきたいと思います。