Turbo Frame下移除图片后编辑内容更新失效问题求助
Turbo Frame产品编辑页移除图片后文本更新丢失问题修复
问题现象
在Turbo Frame的产品编辑页面中,修改文本内容后不刷新页面移除图片,点击Update按钮后页面刷新,文本修改内容丢失,需再次点击才能真正更新。例如修改描述为“456”并移除所有图片后点击Update,页面停留在编辑页,描述变回“123”,再次点击才生效。终端报错ActiveRecord::RecordNotFound,提示找不到指定ID的ActiveStorage::Blob。
错误原因
- 无效表单参数残留:移除图片时仅通过Turbo Stream删除了页面上的图片DOM元素,但对应图片的
signed_id隐藏字段仍留在表单中。提交表单时,这些已被删除图片的signed_id会被发送到后端,后端尝试查找已标记删除的Blob,触发404错误,导致整个更新事务回滚,文本修改也随之失败。 - 异步删除的时间差:使用
purge_later异步删除Blob时,移除操作发起后Blob并未立即被删除,但表单提交时仍携带该Blob的signed_id,后端验证时可能找不到Blob,引发异常。
修复方案
1. 移除图片时同步删除对应隐藏字段
将图片的隐藏字段放到图片的DOM容器内,这样Turbo Stream移除容器时会一并删除隐藏字段,避免提交无效参数:
<div> <%= form.file_field :pictures, multiple: true %> <% if @product.pictures.attached? %> <% @product.pictures.each do |picture| %> <div id="<%= dom_id(picture, :picture) %>"> <%= link_to image_tag(product.picture_as_thumbnail(picture)), picture %> <%= link_to "remove picture", product_picture_path(@product, blob_id: picture.blob_id), data: { turbo_method: :delete} %> <!-- 将hidden_field移入图片容器内 --> <%= form.hidden_field :pictures, multiple: true, value: picture.signed_id %> </div> <% end %> <% end %> </div>
2. 同步删除Blob或过滤无效参数
方案A:同步删除Blob
将purge_later改为purge,确保Blob立即被删除,避免参数校验时出现找不到的情况:
class Products::PicturesController < ApplicationController include ActionView::RecordIdentifier before_action :set_product def destroy @picture = @product.pictures.find(params[:blob_id]) @picture.purge # 替换purge_later为purge respond_to do |format| format.html { redirect_to edit_product_path(@product) } format.turbo_stream { render turbo_stream: turbo_stream.remove(dom_id(@picture, :picture))} end end private def set_product @product = Product.find(params[:product_id]) end end
方案B:过滤无效的signed_id
如果坚持使用异步删除,在后端更新时过滤掉空值和无效的signed_id,避免触发异常:
class ProductsController < ApplicationController # ... 其他代码 ... def update filtered_params = filter_invalid_picture_params(product_params) if @product.update(filtered_params) redirect_to @product, notice: "产品更新成功" else render :edit end end private def product_params params.require(:product).permit(:name, :description, pictures: []) end def filter_invalid_picture_params(params) return params unless params[:pictures].present? valid_pictures = params[:pictures].reject(&:blank?).select do |signed_id| ActiveStorage::Blob.find_signed(signed_id) rescue false end params.merge(pictures: valid_pictures) end end
3. 捕获异常避免页面回滚
在ProductsController的update方法中捕获Blob找不到的异常,返回正确的响应,保留用户输入的文本内容:
def update begin if @product.update(filtered_params) redirect_to @product, notice: "产品更新成功" else render :edit end rescue ActiveRecord::RecordNotFound => e flash.now[:alert] = "部分图片已被删除,请重新提交" render :edit, status: :unprocessable_entity end end
内容的提问来源于stack exchange,提问作者swaeetpotato
相关产品推荐
相关产品推荐

