You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Rails中使用Turbo Frames更新关联模型的最佳实践

解决方案:符合Rails MVC的Turbo交互实现

核心思路

围绕单一职责原则,让InvoicesController负责处理发票关联客户的更新,通过Turbo Streams精准更新目标Turbo Frame,既不破坏表单未保存内容,也不跨控制器修改模型。


1. 重构Turbo Frame结构

在Invoice编辑视图中,给嵌入的Turbo Frame指定唯一ID,方便后续精准替换:

<%= turbo_frame_tag "customer_picker", src: customers_path(invoice_id: @invoice.id) %>

2. 客户列表的选择按钮(Customers#index)

每个客户的「选择」按钮,提交到Invoices#update接口并指定Turbo Stream响应:

<% @customers.each do |customer| %>
  <%= form_with model: @invoice, url: invoice_path(@invoice), method: :patch, data: { turbo_stream: true } do |f| %>
    <%= f.hidden_field :customer_id, value: customer.id %>
    <%= f.submit "选择", class: "btn btn-primary" %>
  <% end %>
<% end %>

同时在CustomersController中接收invoice_id参数,用于初始化视图中的发票实例:

def index
  @customers = Customer.all
  @invoice = Invoice.find(params[:invoice_id]) if params[:invoice_id].present?
end

3. Invoices#update的Turbo Stream响应

创建app/views/invoices/update.turbo_stream.erb,只更新目标Turbo Frame为选中客户的详情视图:

<% if @invoice.update(invoice_params) %>
  <%= turbo_stream.replace "customer_picker" do %>
    <%= render "customers/show", customer: @invoice.customer %>
  <% end %>
<% else %>
  # 处理验证错误,比如重渲染客户列表并提示错误
  <%= turbo_stream.replace "customer_picker" do %>
    <%= render "customers/index", customers: @customers, invoice: @invoice, error: "关联客户失败,请重试" %>
  <% end %>
<% end %>

这里的invoice_params要确保允许customer_id参数:

def invoice_params
  params.require(:invoice).permit(:customer_id, # 其他发票字段...)
end

4. 客户详情视图(Customers#show)

在customers/show.html.erb中,可添加「更换客户」按钮,让Turbo Frame重新加载客户列表:

<h3><%= @customer.name %></h3>
# 客户其他信息展示...
<%= button_to "更换客户", customers_path(invoice_id: params[:invoice_id]), 
              method: :get, data: { turbo_frame: "customer_picker" } %>

为什么符合MVC原则?

  • 所有对Invoice模型的修改都由InvoicesController处理,严格遵循单一职责
  • CustomersController仅负责客户列表和详情的展示,不触及Invoice模型
  • Turbo Streams只更新目标Turbo Frame,完全保留Invoice表单中未保存的输入内容

替代方案:动态切换Turbo Frame的src

如果不想用Turbo Streams渲染视图,也可以直接更新Turbo Frame的src属性,让它自动加载客户详情:

# update.turbo_stream.erb
<% if @invoice.update(invoice_params) %>
  <%= turbo_stream.update "customer_picker", src: customer_path(@invoice.customer, invoice_id: @invoice.id) %>
<% end %>

内容的提问来源于stack exchange,提问作者Kloot

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 15:33:31