关于 turbo-fetch
介绍在 Rails 中结合 Turbo Streams 和 Stimulus 动态更新表单字段的模式,适合需要联动字段或条件表单区块的开发者,无需重新加载页面。
主要功能
- 01通过 PATCH 刷新字段
- 02使用 Turbo Stream 更新 DOM
- 03由 Stimulus 触发请求
- 04集合与成员路由模式
适用场景
- 01选择后刷新下拉选项
- 02显示条件表单区块
- 03无需整页重载即可更新表单
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ rolemodel/ rolemodel-skills --skill turbo-fetch安装后在 Claude Code 中输入/turbo-fetch确认已加载。
详细介绍
Turbo Fetch 技能
本文档介绍 turbo fetch 模式,说明如何使用 Turbo Streams 和 Stimulus,根据用户输入动态更新表单字段。
适用场景
在以下情况下使用 turbo fetch:
- 根据另一个字段的选项更新表单选项
- 动态显示或隐藏条件字段
- 无需重新加载整个页面即可刷新表单的部分内容
- 实现级联下拉菜单或依赖字段
模式概览
turbo fetch 模式由四个组件组成:
- 路由 - 添加 turbo_fetch 端点的路由关注点
- 控制器操作 - 准备数据并渲染 turbo streams 的后端操作
- 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中包装表单构建器,以保留表单上下文 - 授权:应用与 create/update 操作相同的授权规则
- 不要保存:turbo_fetch 操作会创建实例,但绝不保存
- 多次更新:一次响应中可以包含多个 turbo_stream 更新
故障排查
更新未显示:
- 检查目标元素 ID 是否与 turbo_stream 选择器匹配
- 确认 Stimulus 动作是否触发(检查浏览器控制台)
- 确保 turbo_fetch 路由存在(运行
rails routes | grep turbo_fetch)
字段中的数据错误:
- 确认 params 已在
material_params(或等效项)中获准 - 检查模型的计算属性是否返回正确值
授权错误:
- 确保
turbo_fetch操作执行的授权检查与new/create相同
评论
支持 Markdown