本記事は、🔽「Turbo Rails Tutorial」の学習を進めていく際の個人メモです!
🔽 前章の記事はこちら
4章 Turbo Frames and Turbo Stream templates
3章までは、Turbo Drive により、ページ推移にリロードが発生しなくて高速になる、ということまで分かりました。4章では、Turbo Frame と Turbo Stream を使って、newアクションやeditアクションを別ページに推移せず、同一のページで行えるようにします。
まずは、期待する動作になるよう、テスト(test/system/quotes_test.rb)を変更しました。
Turbo Frames とは?
Webページの独立した部分で、JavaScriptを1行も記述せずに、追加、前置、置換、または削除することができます。
turbo_frame_tag
Turbo Frame を作成するには、turbo_frame_tagを使います。
🔽 /quotesページのこの部分につけます。

<%= turbo_frame_tag "first_turbo_frame" do %>
<div class="header">
<h1>見積もり一覧</h1>
<%= link_to "見積もりを作成",
new_quote_path,
class: "btn btn--primary" %>
</div>
<% end %>
🔽 生成されたHTMLには、first_turbo_frame id がついたturbo-frameタグが生成されています。
<turbo-frame id="first_turbo_frame">
<div class="header">
<h1>見積もり一覧</h1>
<a class="btn btn--primary" href="/quotes/new">見積もりを作成</a>
</div>
</turbo-frame>
チュートリアルでは、この状態で「見積もりを作成」ボタンを押すとフレームが消えてブラウザにエラー(Response has no matching <turbo-frame id="first_turbo_frame">element)が出る、とありましたが、エラーは出ず/quotes/newページへ正常に移動しました。また、その際リロードが発生していました。
次に、/quotes/newページの以下の部分に、/quotesページにつけた id と同じturbo_frame_tagをつけてみます。
すると...!
URLは/quotesのまま、同じ id で紐づいたturbo_frame_tagのHTMLが置き換わりました!
チュートリアルでは、turbo_frame_tagの id を変えてみて、マッチしない場合はエラーが出るとありましたが、やはりAjaxではない通常のリクエストになるだけでした。
turbo_frame_tagの考察
- Turbo はリンク先のページに同じ id のフレームがないか探す
- リンク先のページに同じ id があれば、Turbo Frame のコンテンツをリンク先ページの Turbo Frame コンテンツに置き換える
- リンク先のページに同じ id がない場合は、通常のリクエストになる?
data-turbo-frame
Turbo Frame で囲っているHTML内のリンクは、data-turbo-frame属性に別の Turbo Frame のidを指定することで、他のTurbo Frameを動作させることもできます。
/indexの一覧部分と、/newのform部分を"second_frame"というid の Turbo Frame で紐づけておき、以下のようにdata属性で指定すると、「見積もりを作成」をクリックした時"second_frame" idの Turbo Frame が動作するようになっています。
<%= turbo_frame_tag "first_turbo_frame" do %>
<div class="header">
<h1>見積もり一覧</h1>
<%= link_to "見積もりを作成",
new_quote_path,
data: { turbo_frame: "second_frame" },
class: "btn btn--primary" %>
</div>
<% end %>
ページ全体を表す_topという特別なフレーム
先ほどのdata-turbo-frame属性に_topという特別なキーワードを使うと、Turbo Frame ではなくページ全体の更新となります。URLも変わるようになり、デフォルトの Turbo Drive の動作のようになりました。
これは、すべてのページにデフォルトで「ページ全体を Turbo Frame 化」したものが_topとして備わっているからだそうです。
<%= turbo_frame_tag "first_turbo_frame" do %>
<div class="header">
<h1>見積もり一覧</h1>
<%= link_to "見積もりを作成",
new_quote_path,
data: { turbo_frame: "_top" },
class: "btn btn--primary" %>
</div>
<% end %>
dom_id
例えば、一覧ページでそれぞれの見積もりをクリックして「編集」させたい場合、それぞれのidで指定してeditページのturbo_frame_tagに紐付けすることができます。
こんな感じのturbo_frame_tagが作成できます。
<%= turbo_frame_tag "quote_#{@quote.id}" do %>
...
<% end %>
しかし、上記のコードでは文字列を使っており、スペルミスによりバグが入り込みやすいです。そこで、dom_idヘルパーを使うことで、上記のコードと全く同じものを作成しながら、格段にすっきりと書けます。
<%= turbo_frame_tag dom_id(@quote) do %>
...
<% end %>
さらに、dom_idですらも省略が可能で、最終的にこうなります。
<%= turbo_frame_tag @quote do %>
...
<% end %>
dom_id、一意のIDに変換するのに便利ですね。
dom_id(@quote) # => "quote_1"
dom_id(Quote.new) # => "new_quote"
# 任意の文字列を連結させることもできる
dom_id(Quote.new, "prefix") # "prefix_new_quote"
dom_idについては、以下の記事にも詳しく載っています。
Turbo Streams とは?
削除ボタンを押してみると、TURBO_STREAMというものが表示されるのが確認できます。Rails7のフォームは、TURBO_STREAM形式で送信されるそうです。
Started DELETE "/quotes/309456473" for ::1 at 2023-04-23 00:43:00 +0900
Processing by QuotesController#destroy as TURBO_STREAM
先ほどから利用してきたturbo_frame_tagでは、リンクのクリック時と、フォームの送信時に1ヶ所の書き換えはできるのですが、それ以外の操作はturbo_streamを使います。
今回のチュートリアルで言うと、ある見積もりの「編集」を押してeditフォームに書き換えられたページで、他の見積もりの「削除」を行う場合は、destroyアクション内でturbo_streamを使います。
def destroy
@quote.destroy
respond_to do |format|
format.html { redirect_to quotes_path, notice: "Quote was successfully destroyed." }
format.turbo_stream
end
end
respond_toで JavaScript が無効化されている場合でも通常のフォーム送信で削除ができるようになっています。
ブラウザの JavaScript を無効/有効で切り替えて動作を試すと、面白いです!
4章で完成したもの
この章で学んだものを色々と組み合わせて、最終的にこんなページができました!
同時に編集ができるの、気持ちいですね!
「やめる」ボタンの挙動も、追加コードはたったの1行。よく噛み砕かないと裏側で何が起きているのか理解するのは結構難しかったですが、分かると楽しいですね。
(にしても、JavaScriptを1行も書かずにこれ実現できているの、凄い!!)
これにて4章は終わりです。次は5章をやっていきます。Action Cable を使ってリアルタイムアップデートを作るそうです。楽しみ〜🙌




