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

如何在使用remote:true的simple_form表单校验失败时展示错误信息

问题根因

你当前的实现有两个核心问题:

  1. 校验失败时控制器给JS请求返回了JSON格式数据,没有返回可以更新页面的JS脚本或带错误的表单片段
  2. 大概率Rails UJS未正确启用,导致remote: true的表单没有走AJAX提交,而是普通同步提交,所以浏览器直接渲染了返回的JSON内容

修复步骤

第一步:确认Rails UJS已正确启用

Rails 6默认使用webpacker管理JS依赖,检查app/javascript/packs/application.js文件中是否存在以下代码,没有的话加上:

import Rails from "@rails/ujs"
Rails.start()

第二步:抽离表单为局部模板

把模态框里的表单代码单独抽成局部模板app/views/orders/_form.html.erb,方便复用:

<%= simple_form_for(order, wrapper: :horizontal_form,
  wrapper_mappings: {
    boolean:       :horizontal_boolean,
    check_boxes:   :horizontal_collection,
    date:          :horizontal_multi_select,
    datetime:      :horizontal_multi_select,
    file:          :horizontal_file,
    radio_buttons: :horizontal_collection,
    range:         :horizontal_range,
    time:          :horizontal_multi_select
  }, remote: true) do |f| %>
  <%= f.error_notification %>
  <%= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present? %>

  <div class="form-inputs">
    <%= f.input :customerName %>
    <%= f.input :customerAddress %>
    <%= f.input :customerEmail %>
    <%= f.input :customerPhoneNumber %>
    <%= f.input :restaurantName %>
    <%= f.input :restaurantAddress %>
    <%= f.input :expectedPickupTime %>
    <%= f.input :expectedDeliveyTime %>
    <%= f.input :deliveryInstruction %>
    <%= f.input :paymentMethod %>
    <%= f.input :deliveryFee %>
    <%= f.association :employee %>
  </div>

  <div class="form-actions">
    <%= f.button :submit %>
  </div>
<% end %>

原来模态框里的表单位置替换为:

<%= render 'form', order: order %>

第三步:修改控制器响应逻辑

修改orders_controller.rb的create方法,把校验失败时的JS响应改为渲染JS模板,而不是JSON:

def create
  @order = Order.new(order_params)

  respond_to do |format|
    if @order.save
      format.html { redirect_to @order, notice: 'Order was successfully created.' }
      format.json { render :show, status: :created, location: @order }
      format.js # 会默认渲染app/views/orders/create.js.erb
    else
      format.html { render :new }
      format.json { render json: @order.errors, status: :unprocessable_entity }
      format.js   { render :new, status: :unprocessable_entity } # 渲染new.js.erb返回带错误的表单
    end
  end
end

第四步:添加JS响应模板

新建app/views/orders/create.js.erb,处理创建成功后的逻辑:

// 关闭模态框,可根据你的模态框实际ID调整选择器
$('#exampleModalLongTitle').closest('.modal').modal('hide');
// 可选:添加成功提示、刷新订单列表等逻辑
alert('订单创建成功');
// 也可以直接刷新页面:window.location.reload();

新建app/views/orders/new.js.erb,处理校验失败时替换模态框内的表单为带错误信息的版本:

// 替换模态框body内的内容为带错误的表单
$('.modal-body').html('<%= j render "form", order: @order %>');

这里的j是escape_javascript的别名,用来转义HTML特殊字符避免JS语法错误。


可选方案:前端处理错误(无需修改服务端渲染逻辑)

如果不想改服务端渲染逻辑,也可以保留原来返回JSON的写法,添加JS监听表单的ajax错误事件,手动渲染错误:

// 放在application.js里或者对应页面的JS中
document.addEventListener('DOMContentLoaded', () => {
  // 替换为你表单的实际ID,默认simple_form生成的ID是new_order
  const orderForm = document.querySelector('#new_order');
  if (orderForm) {
    orderForm.addEventListener('ajax:error', (event) => {
      const [errors, status, xhr] = event.detail;
      // 清空之前的错误提示
      document.querySelectorAll('.error-notification, .invalid-feedback').forEach(el => el.remove());
      document.querySelectorAll('.form-group.is-invalid').forEach(el => el.classList.remove('is-invalid'));
      // 渲染全局错误
      if (errors.base) {
        const errorAlert = `<div class="alert alert-danger error-notification">${errors.base.join(', ')}</div>`;
        orderForm.insertAdjacentHTML('afterbegin', errorAlert);
      }
      // 渲染每个字段的错误
      Object.keys(errors).forEach(field => {
        const inputEl = orderForm.querySelector(`[name="order[${field}]"]`);
        if (inputEl) {
          const formGroup = inputEl.closest('.form-group');
          formGroup.classList.add('is-invalid');
          const errorMsg = `<div class="invalid-feedback">${errors[field].join(', ')}</div>`;
          inputEl.insertAdjacentHTML('afterend', errorMsg);
        }
      });
    });
  }
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.21 23:54:03