如何在使用remote:true的simple_form表单校验失败时展示错误信息
问题根因
你当前的实现有两个核心问题:
- 校验失败时控制器给JS请求返回了JSON格式数据,没有返回可以更新页面的JS脚本或带错误的表单片段
- 大概率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
相关产品推荐
相关产品推荐

