はじめに
本記事は錆びかけたRailsの知識を頑張ってアップデートするアドベントカレンダー8日目です。
今日は、猫Rails様のTurboのチュートリアルでRails7のTurbo Streamを勉強していたときに浮かんだ疑問についてまとめます。
疑問点
Turbo Streamと「○○.js.erb」との違いは?
flash.noticeとflash.notice.nowとの違いは?
指定ページの強制リロードは本当にできる?
Turbo Streamと「○○.js.erb」との違いは?
非同期の実装について、Rails6までは、Railsでbuttonタグやaタグを作ってくれるメソッドbutton_tagやlink_toなどにremote: true
というオプションをつけ、そこからのリクエストを非同期にするという方法がありました。
<%= hoge_path, method: :post, remote: true do %>
<%= image_tag "path-to-image", alt: "Like" %>
<% end %>
そして、○○.html.erbではなく○○.js.erbというファイルを用意し、これをrender時に指定することで、JavaScriptを実行してDOMを書き換えてしまうという方法です。
document.getElementById('like-btn<%= @hoge.id %>').innerHTML = "<%= j(render partial: 'likes/like', locals: {hoge: @hoge}) %>"
Rubyのインスタンス変数をJavaScriptに渡して整形、それをクライアントに返却するという形ですね。
この方法でも非同期で見た目を更新でき、いい感じのUXを実現できます。
Turbo Streamsを使うメリット
リクエストをきっかけに複数箇所を更新するためのレールがひかれた、ということでしょうか。
JavaScriptを書くことでもさまざまな挙動は実現できますが、特定のやり方が用意されて、同じチームの人がすぐにコンセンサスを取って書けるのはスピードにつながりそうです。
Turbo Frameでどの部分を置き換えるのかを指定できるturbo_frame_tagのように、turbo Streamでもわかりやすい書き方ができるともっと良さそうですね。現状、id
をつけているだけなので若干混乱しそうです。
flash.noticeとflash.notice.nowとの違いは?
Turboとはあんまり関係ないのですが、以下の記述がよくわかりませんでした。
Flashは通常だとリダイレクト時に使うのでflash.noticeを使って設定する。でもTurbo Streamsでは今回のリクエストに対してFlashを設定したい。そういう場合には今回のリクエストに対してだけ有効なflash.now.noticeを利用するよ。
そこで、通常のflashとflash.nowとの違いをまとめておきます。
flash
使用場面:
flashは、主にリダイレクトが伴うアクションで使用されます。つまり、ユーザーが現在のページから別のページにリダイレクトされる場合です。
動作:
flashメッセージはリクエスト間で持続し、リダイレクト後のページで表示されます。その後、次のリクエストで消えます。
def update
if @user.update(user_params)
flash[:notice] = "プロファイルが更新されました。"
redirect_to @user
else
render :edit
end
end
flash.now
使用場面:
flash.nowは、リダイレクトを伴わないアクション、つまり現在のリクエストの中で完結するアクションで使用されます。これは、ページの再描画が発生しないAjaxリクエストやTurbo Streamリクエストに特に有効です。
動作:
flash.nowメッセージは、現在のリクエストでのみ有効で、次のリクエストには持続しません。
def create
@comment = Comment.new(comment_params)
if @comment.save
flash.now[:notice] = "コメントが追加されました。"
render turbo_stream: turbo_stream.append('comments', @comment)
else
render :new
end
end
指定ページの強制リロードは本当にできる?
指定ページの強制リロード
🐱 Turboと相性が悪いJavaScriptのライブラリを使う際に、指定のページだけフルリロードさせたい場合があるよ。そんな時は以下のような要素を追加すると、ページを開いた際にwindow.location.reload()を実行させて強制的にフルリロードさせることができるよ。・・・
追加する要素はheadタグの中身でした(以下参照)。
<head>
...
<meta name="turbo-visit-control" content="reload">
</head>
Railsではheadタグはapplication.html.erbに含まれており、これは共通のテンプレートなので特定のページのみ追加するのは難しいのでは?と思いました。
解決策
結論、content_for
メソッドを利用することで実現可能でした。
application.html.erbでheadタグの中身にyieldを追加します。
<!-- application.html.erb -->
<head>
...
<%= yield :head %>
</head>
その後任意のビューでcontent_forを使って、必要なmetaタグを追加できます。
<!-- 例えば、特定のビューのファイル -->
<% content_for :head do %>
<meta name="turbo-visit-control" content="reload">
<% end %>
このコードは、そのビューがレンダリングされるときにのみmetaタグがheadセクションに挿入されます。