mcpField
T
turbo-fetch

turbo-fetch

github.com/rolemodel開発7123 回インストール2 日前に更新
リポジトリ

turbo-fetch について

Turbo Streams と Stimulus を使い、Rails のフォーム項目を動的に更新するパターンを解説するスキルです。ページ全体を再読み込みせず、連動項目や条件付きセクションを実装する開発者向けです。

主な機能

  1. 01PATCH による項目更新
  2. 02Turbo Stream による DOM 更新
  3. 03Stimulus 起点のリクエスト
  4. 04コレクション/メンバールート

活用シーン

  1. 01選択に応じたドロップダウン更新
  2. 02条件付きフォームセクションの表示
  3. 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つのコンポーネントで構成されます。

  1. ルート - turbo_fetch エンドポイントを追加するルーティングの仕組み
  2. コントローラーアクション - データを準備し、Turbo Stream をレンダリングするバックエンドアクション
  3. Stimulus コントローラー - PATCH リクエストをトリガーするフロントエンドコントローラー
  4. 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

ヒント

  1. 対象 ID:対象要素に一意で安定した ID があることを確認します
  2. フォームのコンテキスト:Turbo Stream ビューでは simple_form_for がフォームビルダーをラップし、フォームのコンテキストを維持します
  3. 認可:作成/更新アクションと同じ認可を適用します
  4. 保存しない:turbo_fetch アクションはインスタンスを作成しますが、保存はしません
  5. 複数の更新:1つのレスポンスに複数の turbo_stream 更新を含めることができます

トラブルシューティング

更新が表示されない場合:

  • 対象要素の ID が turbo_stream セレクターと一致していることを確認します
  • Stimulus アクションが実行されていることを確認します(ブラウザーのコンソールを確認してください)
  • turbo_fetch ルートが存在することを確認します(rails routes | grep turbo_fetch を実行してください)

フィールドのデータが誤っている場合:

  • material_params(または同等のもの)でパラメーターが許可されていることを確認します
  • モデルの計算プロパティが正しい値を返すことを確認します

認可エラーが発生する場合:

  • turbo_fetch アクションで new/create と同じ認可処理が実行されることを確認します

コメント