Rails 7自定义控制器中Turbo Stream渲染后HTML不更新问题
Rails 7.0.4 Turbo Stream 无页面更新问题
问题背景
运行Rails 7.0.4,尝试在自定义控制器中通过Turbo Frame/Stream实现表单字段更新失败时显示错误信息。api_update方法由Stimulus函数在用户点击表单外部时触发,控制器能正常接收数据、数据库操作也无异常,但页面始终没有变化。
开发日志信息
日志无报错,但显示Turbo Stream文件被渲染两次:
20:55:37 web.1 | TRANSACTION (0.2ms) ROLLBACK 20:55:37 web.1 | ↳ app/controllers/organization_controller.rb:34:in `api_update' 20:55:37 web.1 | Rendering organization/organization_edit_form_error.turbo_stream.erb 20:55:37 web.1 | Rendered organization/_error.html.erb (Duration: 0.5ms | Allocations: 134) 20:55:37 web.1 | Rendered organization/organization_edit_form_error.turbo_stream.erb (Duration: 0.9ms | Allocations: 272) 20:55:37 web.1 | Completed 200 OK in 37ms (Views: 1.6ms | ActiveRecord: 4.0ms | Allocations: 10991)
Network响应内容
开发者工具中可见符合预期的Turbo Stream响应:
<turbo-stream action="replace" target="organization_715728df-0095-4dae-b2c4-9b6911003bfd_error"><template> test <div role="alert"> Could not edit organization due to the following issues: <ul> <li>Website is not a valid URL</li> </ul> </div> </template></turbo-stream>
核心代码片段
controllers/organization_controller.rb(节选)
class OrganizationController < ApplicationController def api_update if organization.update(api_organization_params) respond_to do |format| format.html { render :edit, organization: @organization } end else respond_to do |format| format.turbo_stream { render :organization_edit_form_error } end end end end
/views/organization/edit.html.erb
<div data-controller="organizations" class="mt-10"> <%= turbo_frame_tag "#{dom_id(@organization)}_error" do %> <%= render partial: 'error', organization: @organization %> <% end %> <%= render 'form', organization: @organization %> </div>
/views/organization/organization_edit_form_error.turbo_stream.erb
<%= turbo_stream.replace "#{dom_id(@organization)}_error", partial: "error", organization: @organization %>
/views/organization/_error.html.erb
<% if @organization.errors.any? %> <div role="alert"> Could not edit organization due to the following issues: <ul> <% @organization.errors.each do |error| %> <li><%= error.full_message %></li> <% end %> </ul> </div> <% end %>
已尝试的操作
- 在
_error.html.erb的if @organization.errors.any?块中添加测试输出,确认@organization存在且包含错误,日志能显示预期输出 - 尝试不使用Turbo Frame包裹直接替换元素,无效
- 在控制器
respond_to块中添加format.html作为降级方案,问题依旧
解决方案
1. 修正Stimulus请求的请求头
Turbo Stream要求请求的Accept头包含text/vnd.turbo-stream.html,否则Rails无法正确处理响应。确保你的Stimulus函数发起请求时设置正确的头信息:
// organizations_controller.js示例代码 async save() { const response = await fetch('/organizations/api_update', { method: 'PATCH', headers: { 'Content-Type': 'application/json', 'Accept': 'text/vnd.turbo-stream.html, text/html, application/json', 'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content }, body: JSON.stringify(/* 提交的数据 */) }); }
2. 确认目标元素ID完全匹配
检查页面上的Turbo Frame ID和响应中的target值是否完全一致,避免拼写错误、空格或大小写问题。可以通过浏览器开发者工具直接查看页面元素ID进行核对。
3. 修复控制器变量引用问题
控制器中使用organization.update,但渲染视图依赖@organization,需确保organization方法正确赋值给实例变量:
# 在OrganizationController的private部分添加 def organization @organization ||= Organization.find(params[:id]) end
4. 简化控制器Turbo Stream渲染逻辑
日志中出现重复渲染的情况,可简化控制器代码,直接返回Turbo Stream响应:
def api_update if organization.update(api_organization_params) render :edit, organization: @organization else render turbo_stream: turbo_stream.replace("#{dom_id(@organization)}_error", partial: "error", organization: @organization) end end
5. 确认Turbo库已正确加载
检查application.js中是否导入了Turbo:
import "@hotwired/turbo-rails"
内容的提问来源于stack exchange,提问作者Tim Chipperfield
相关产品推荐
相关产品推荐

