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

Rails 7 Turbo问题求助:Todo增删需刷新才生效

Rails 7 Turbo Todo应用:创建/删除后需刷新页面的问题修复

问题场景

在使用Rails 7的Turbo开发Todo应用时,碰到以下异常:

  • 创建Todo对象后,必须刷新页面才能看到新条目
  • 删除Todo时,同样需要刷新页面才会从列表中消失
    尝试过method: :delete和data: {turbo_method: :delete}参数,问题仍未解决,怀疑变量使用有误但无法准确定位。

核心问题分析

  1. Turbo Frame ID冲突:每个Todo条目都使用相同的todos作为Turbo Frame的ID,导致Turbo无法精准定位单个条目进行更新/删除操作。
  2. Turbo Stream目标错误:创建动作中turbo_stream.append的目标是重复的todos frame,而非包裹所有Todo的容器;删除动作中turbo_stream.remove用法错误,不需要传入partial,且目标ID不正确。
  3. 冗余的remote: true参数:Rails 7的Turbo默认处理AJAX式提交,表单和删除按钮上的remote: true属于多余配置,可能干扰Turbo的正常工作。

修正后的代码

控制器代码(todos_controller.rb)

class TodosController < ApplicationController
  before_action :set_todo, only: [:show, :edit, :update, :destroy]
  
  def index
    @todos = Todo.all
    @todo = Todo.new
  end
  
  def show
  end
  
  def new
    @todo = Todo.new
  end
  
  def create
    @todo = Todo.new(todo_params)
    if @todo.save
      # 追加到包裹所有todo的容器#todos-container
      render turbo_stream: turbo_stream.append('todos-container', partial: 'todo_partial', locals: { todo: @todo }),
             notice: 'Task was successfully created.'
    else
      render :new
    end
  end
  
  def update
    if @todo.update(todo_params)
      # 替换单个todo的唯一frame
      render turbo_stream: turbo_stream.replace("todo-#{@todo.id}", partial: 'todo_partial', locals: { todo: @todo }),
             notice: 'Task was successfully updated.'
    else
      render :edit
    end
  end
  
  def edit
  end
  
  def destroy
    @todo.destroy
    # 删除单个todo的唯一frame,仅需传入目标ID
    render turbo_stream: turbo_stream.remove("todo-#{@todo.id}"),
           notice: 'Task was successfully deleted.'
  end
  
  private
  
  def set_todo
    @todo = Todo.find(params[:id])
  end
  
  def todo_params
    params.require(:todo).permit(:name, :description, :status)
  end
end

Todo Partial(_todo_partial.html.erb)

给每个Todo分配唯一的Turbo Frame ID,避免冲突:

<%= turbo_frame_tag "todo-#{todo.id}" do %>
  <div class="todo-item">
    <h3><%= todo.name %></h3>
    <p>Description: <%= todo.description %></p>
    <p>Status: <%= todo.status %></p>
    <%= link_to "Edit", edit_todo_path(todo) %>
    <%= button_to "Delete", todo_path(todo), data: { turbo_method: :delete } %>
  </div>
<% end %>

Index视图(index.html.erb)

添加唯一的容器#todos-container,用于接收新创建的Todo:

<h1>Index: List all todos</h1>

<!-- 包裹所有todo的容器,用于Turbo Stream追加操作 -->
<div id="todos-container">
  <% @todos.each do |todo| %>
    <%= render partial: 'todo_partial', locals: { todo: todo } %>
  <% end %>
</div>

<!-- 创建Todo的表单 -->
<%= render partial: 'form_partial', locals: { todo: @todo } %>

Form Partial(_form_partial.html.erb)

移除冗余的remote: true参数,添加错误提示:

<%= form_for(@todo, html: { class: 'bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4' }) do |f| %>
  <% if @todo.errors.any? %>
    <div class="alert alert-danger">
      <% @todo.errors.full_messages.each do |msg| %>
        <p><%= msg %></p>
      <% end %>
    </div>
  <% end %>

  <div class="mb-4">
    <%= f.label :name, class: 'block text-gray-700 text-sm font-bold mb-2' %>
    <%= f.text_field :name, class: 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline' %>
  </div>

  <div class="mb-4">
    <%= f.label :description, class: 'block text-gray-700 text-sm font-bold mb-2' %>
    <%= f.text_area :description, class: 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline' %>
  </div>

  <div class="mb-4">
    <%= f.label :status, class: 'block text-gray-700 text-sm font-bold mb-2' %>
    <%= f.select :status, options_for_select(Todo.statuses.keys.to_a, @todo.status), {}, class: 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline' %>
  </div>

  <div class="flex items-center justify-between">
    <%= f.submit 'Submit', class: 'bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline' %>
  </div>
<% end %>

Show视图(show.html.erb)

移除冗余的remote: true参数:

<h1>Show: Specific details about a certain todo</h1>
<p><strong>Title:</strong> <%= @todo.name %></p>
<p><strong>Description:</strong> <%= @todo.description %></p>
<p><strong>Status:</strong> <%= @todo.status %></p>

<%= link_to "Edit", edit_todo_path(@todo) %>
<%= button_to "Delete", todo_path(@todo), data: { turbo_method: :delete } %>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 05:54:58