turbo-fetch について
Turbo Streams と Stimulus を使い、Rails のフォーム項目を動的に更新するパターンを解説するスキルです。ページ全体を再読み込みせず、連動項目や条件付きセクションを実装する開発者向けです。
主な機能
- 01PATCH による項目更新
- 02Turbo Stream による DOM 更新
- 03Stimulus 起点のリクエスト
- 04コレクション/メンバールート
活用シーン
- 01選択に応じたドロップダウン更新
- 02条件付きフォームセクションの表示
- 03ページ全体を再読み込みしないフォーム更新
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ rolemodel/ rolemodel-skills --skill turbo-fetchインストール後、Claude Code で/turbo-fetchを入力して読み込まれたか確認します。
詳細
Turbo Fetch スキル
このスキルでは、Turbo Streams と Stimulus を使用して、ユーザー入力に基づきフォームフィールドを動的に更新する Turbo Fetch パターンについて説明します。
使用する場面
次のような場合に Turbo Fetch を使用します。
- 別のフィールドの選択内容に基づいてフォームの選択肢を更新する
- 条件付きフィールドを動的に表示/非表示にする
- ページ全体を再読み込みせずにフォームの一部を更新する
- 連動するドロップダウンや依存フィールドを実装する
パターンの概要
Turbo Fetch パターンは、次の4つのコンポーネントで構成されます。
- ルート -
turbo_fetchエンドポイントを追加するルーティングの仕組み - コントローラーアクション - データを準備し、Turbo Stream をレンダリングするバックエンドアクション
- Stimulus コントローラー - PATCH リクエストをトリガーするフロントエンドコントローラー
- Turbo Stream ビュー - 特定の DOM 要素を更新するテンプレート
実装手順
1. ルーティングの仕組みを追加する(まだ存在しない場合)
config/routes.rb で、turbo_fetch の仕組みが存在することを確認します。
concern :turbo_fetch do
patch :turbo_fetch, on: :collection
end
次に、リソースに適用します。
resources :materials, concerns: %i[turbo_fetch]
これにより、ルート PATCH /materials/turbo_fetch が作成されます。
2. コントローラーアクションを実装する
コントローラーに turbo_fetch アクションを追加します。
class MaterialsController < ApplicationController
def turbo_fetch
@material = authorize Material.new(material_params)
# The view will handle the turbo stream responses
end
private
def material_params
params.require(:material).permit(:type, :substance, ...)
end
end
要点:
- 送信されたパラメーターを使って新しいインスタンスを作成します(データベースには保存しません)
- 必要に応じて認可を実行します
- インスタンスは、更新する選択肢を決定するために Turbo Stream ビューで使用されます
3. Turbo Stream ビューを作成する
app/views/[resource]/turbo_fetch.turbo_stream.slim を作成します。
= simple_form_for @material do |f|
= turbo_stream.update 'substance-field' do
= f.input :substance, collection: @material.substances
= turbo_stream.replace 'material_details', partial: dimension_fields_partial_path, locals: { f: }
使用可能な Turbo Stream アクション:
turbo_stream.update- 要素内のコンテンツを置き換えますturbo_stream.replace- 要素全体を置き換えますturbo_stream.append- コンテンツを末尾に追加しますturbo_stream.prepend- コンテンツを先頭に追加しますturbo_stream.remove- 要素を削除します
4. Stimulus コントローラーを使ってフォームを設定する
フォームに Stimulus コントローラーを追加します。
= simple_form_for resource, data: { controller: 'turbo-fetch', turbo_fetch_url_value: turbo_fetch_materials_url } do |f|
.form-row
= f.input :type, input_html: { data: { action: "turbo-fetch#perform" } }
#substance-field.flexible
= f.input :substance, collection: f.object.substances
#material_details
= render dimension_fields_partial_path, f: f
主な属性:
data-controller="turbo-fetch"- Stimulus コントローラーを有効にしますdata-turbo-fetch-url-value- PATCH 先の URL です(デフォルトはフォームのアクションに/turbo_fetchを追加したものです)data-action="turbo-fetch#perform"- フィールドの変更時に Fetch をトリガーします
5. Stimulus コントローラーが存在することを確認する
turbo_fetch_controller.js が app/javascript/controllers/turbo_fetch_controller.js に存在することを確認します。
import { Controller } from '@hotwired/stimulus'
import { patch } from '@rails/request.js'
export default class extends Controller {
static values = { url: String, count: Number }
async perform({ params: { url: urlParam, query: queryParams } }) {
const body = new FormData(this.element)
if (queryParams) Object.keys(queryParams).forEach(key => body.append(key, queryParams[key]))
const response = await patch(urlParam || this.urlValue, { body, responseKind: 'turbo-stream' })
if (response.ok) this.countValue += 1
}
}
コードベースの例
Materials の例
ルート:
resources :materials, concerns: %i[duplication turbo_fetch]
コントローラー:
def turbo_fetch
@material = authorize Material.new(material_params)
end
ビュー(turbo_fetch.turbo_stream.slim):
= simple_form_for @material do |f|
= turbo_stream.update 'substance-field' do
= f.input :substance, collection: @material.substances, as: :tom_select, allow_create: true
= turbo_stream.replace 'material_details', partial: dimension_fields_partial_path, locals: { f: }
フォーム:
= simple_form_for resource, data: { controller: 'turbo-fetch', turbo_fetch_url_value: turbo_fetch_materials_url } do |f|
= f.input :type, input_html: { data: { action: "turbo-fetch#perform" } }
#substance-field.flexible
= f.input :substance, collection: f.object.substances
#material_details
= render dimension_fields_partial_path, f: f
よくあるパターン
パターン1:依存ドロップダウン
種類を選択したときに、別のフィールドで使用できる選択肢を更新します。
- トリガーフィールドに
data-action="turbo-fetch#perform"を設定します - 対象フィールドに一意の ID(例:
#substance-field)を設定します - Turbo Stream で対象を新しいコレクションに更新します
パターン2:条件付きフィールドセクション
選択内容に基づいてフォームセクション全体を表示/非表示にします。
turbo_stream.replaceを使ってセクション全体を入れ替えます- 選択された値に応じて異なるパーシャルをレンダリングします
パターン3:メンバールートとコレクションルート
多くの turbo_fetch ルートは :collection にありますが、ID を持つネストされたリソースでは、次のようにします。
resources :custom_parts, concerns: %i[turbo_fetch] do
patch :turbo_fetch, on: :member # For child items with IDs
end
ヒント
- 対象 ID:対象要素に一意で安定した ID があることを確認します
- フォームのコンテキスト:Turbo Stream ビューでは
simple_form_forがフォームビルダーをラップし、フォームのコンテキストを維持します - 認可:作成/更新アクションと同じ認可を適用します
- 保存しない:
turbo_fetchアクションはインスタンスを作成しますが、保存はしません - 複数の更新:1つのレスポンスに複数の
turbo_stream更新を含めることができます
トラブルシューティング
更新が表示されない場合:
- 対象要素の ID が turbo_stream セレクターと一致していることを確認します
- Stimulus アクションが実行されていることを確認します(ブラウザーのコンソールを確認してください)
- turbo_fetch ルートが存在することを確認します(
rails routes | grep turbo_fetchを実行してください)
フィールドのデータが誤っている場合:
material_params(または同等のもの)でパラメーターが許可されていることを確認します- モデルの計算プロパティが正しい値を返すことを確認します
認可エラーが発生する場合:
turbo_fetchアクションでnew/createと同じ認可処理が実行されることを確認します