本記事は、🔽「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 を使ってリアルタイムアップデートを作るそうです。楽しみ〜🙌