You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

Rails 8中切换display属性显示隐藏表单时Turbo失效的问题求助

Rails 8中切换display属性显示隐藏表单时Turbo失效的问题求助

我太懂你这种头疼的感觉了——明明Turbo删功能用得顺顺的,结果一个显示隐藏表单的操作就把Turbo的绑定搞崩了,试了rebind还自动隐藏,简直离谱!结合你贴的代码,我给你分析下问题根源和几个可行的解决思路:

问题根源

Turbo在页面加载时会自动扫描并绑定所有可见的表单/链接,但如果表单一开始是display:none的,Turbo可能跳过了它的事件监听器初始化。等你用Stimulus把表单显示出来后,Turbo的绑定没跟上,导致表单只能走普通HTML提交,触发全页刷新。


解决思路(按推荐优先级排序)

1. 用CSS替代display:none,让Turbo能正确初始化表单

既然visibility会留空白行,那我们换个隐藏方式:用height:0 + overflow:hidden来把表单压缩成0高度,既不占空间,又能让Turbo在页面加载时就绑定它。

步骤1:添加CSS类
在你的全局样式文件(比如app/assets/stylesheets/application.tailwind.css)里加两个类:

.hidden-edit-form {
  height: 0;
  overflow: hidden;
  margin: 0;
  padding: 0;
  border: 0;
}

.visible-edit-form {
  height: auto;
  overflow: visible;
  margin: 1rem 0; /* 还原你需要的间距 */
  padding: 0;
}

步骤2:修改_ingredient.html.erb的表单容器
把原来的style="display:none"换成CSS类:

<div id="edit_form_<%= ingredient.id %>" data-ingredient-id="<%= ingredient.id %>" class="hidden-edit-form">
  <%= render "ingredients/form", ingredient: ingredient, recipe: @recipe, form_type: "edit_form" %>
</div>

步骤3:修改Stimulus控制器的toggle方法
不用操作display,直接切换CSS类:

// app/javascript/controllers/ingredients_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  toggleEditForm(event) {
    event.preventDefault()
    const ingredientId = event.currentTarget.dataset.ingredientId
    const editForm = this.element.querySelector(`#edit_form_${ingredientId}`)
    
    // 切换类即可,Turbo已经在页面加载时绑定了表单
    editForm.classList.toggle("hidden-edit-form")
    editForm.classList.toggle("visible-edit-form")
  }

  // 你的deleteIngredient等其他方法不变
}

这个方法最省心,不用碰Turbo的绑定逻辑,完全靠CSS绕开display:none的坑,还不会留空白行。


2. 手动让Turbo绑定显示后的表单

如果不想改CSS,那就在Stimulus显示表单后,手动调用Turbo的API重新绑定这个表单。

修改Stimulus控制器

// app/javascript/controllers/ingredients_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  toggleEditForm(event) {
    event.preventDefault()
    const ingredientId = event.currentTarget.dataset.ingredientId
    const editForm = this.element.querySelector(`#edit_form_${ingredientId}`)
    
    if (editForm.style.display === "none" || !editForm.style.display) {
      editForm.style.display = "block"
      // 关键:手动让Turbo绑定这个表单
      const form = editForm.querySelector("form")
      if (form) Turbo.connectForm(form)
    } else {
      editForm.style.display = "none"
    }
  }
}

这个方法要注意:别在绑定后触发任何会让表单隐藏的Turbo事件(比如误触turbo:submit-end的隐藏逻辑),你之前遇到的自动隐藏大概率是因为rebind时触发了多余的事件,现在手动绑定单个表单就不会有这个问题。


3. 用Turbo Frame动态加载编辑表单

更彻底的Turbo-native方案:一开始不渲染编辑表单,点击编辑按钮时用Turbo Frame动态拉取表单内容,这样Turbo会自动处理所有绑定。

步骤1:修改_ingredient.html.erb的编辑按钮和表单容器

<turbo-frame id="<%= dom_id(ingredient) %>" data-controller="ingredients" >
  <!-- 原来的显示内容不变 -->
  <div class="border-1 border-dotted grid grid-cols-9 gap-4">
    <!-- ... 其他内容 ... -->
    <div>
      <%= link_to edit_recipe_ingredient_path(@recipe, ingredient, locale: I18n.locale), 
                 data: { action: "click->ingredients#loadEditForm", ingredient_id: ingredient.id } do %>
        <%= render_icon 'pencil_square_outline', classes: 'h-4 w-4' %>
      <% end %>
    </div>
  </div>
  <!-- 空的Turbo Frame用来加载表单 -->
  <turbo-frame id="edit_form_<%= ingredient.id %>" style="display: none;"></turbo-frame>
</turbo-frame>

步骤2:Stimulus控制器加载表单

loadEditForm(event) {
  event.preventDefault()
  const ingredientId = event.currentTarget.dataset.ingredientId
  const frame = document.getElementById(`edit_form_${ingredientId}`)
  
  // 加载表单并显示
  frame.src = event.currentTarget.href
  frame.style.display = "block"
}

步骤3:IngredientsController的edit动作返回Turbo Frame

# app/controllers/ingredients_controller.rb
def edit
  @ingredient = Ingredient.find(params[:id])
  @recipe = @ingredient.recipe
  respond_to do |format|
    format.turbo_stream { render turbo_stream: turbo_stream.replace("edit_form_#{@ingredient.id}", partial: "ingredients/form", locals: { ingredient: @ingredient, recipe: @recipe, form_type: "edit_form" }) }
    format.html
  end
end

这个方案最符合Turbo的设计理念,适合复杂的编辑场景,但需要改的代码多一点。


最后检查点

确保你的form_with没有禁用Turbo:默认form_with是开启Turbo的,但如果你的form里加了data-turbo="false"就会失效,检查下_form.html.erb里的form_with有没有这个属性。

先试试第一个CSS方案,应该能最快解决你的问题!

火山引擎 最新活动