【发布时间】:2023-03-28 20:04:01
【问题描述】:
有很多资源处理 Rails 表单和简单的 ajax crud 接口,比如那些著名的 Post -> comment 示例。但是,我的问题有点复杂,我似乎无法让它工作。
我有一个订单表格,它可以包含几个 line_items。在 ryan bates 的 nested_form gem 的帮助下,在提交任何内容之前添加和删除这些 line_items。这一切都很好,并且完美无缺。
现在我想添加一些动态行为。当某些 line_item 输入字段更改时,应更新总计和其他计算。当为该 line_item 选择另一个产品时,其价格应在提交表单之前更新并显示。像那样的东西。我的第一种方法是在 JavaScript 中进行这些计算,它在工作时很快让我意识到我正在将我在应用程序模型中定义的所有逻辑加倍。另外,在 JS 中这样做很麻烦,因为您无权访问实例变量。
经过一番思考,一旦有人更改了某些输入值,我想提交并刷新那些 fields_for line_item 部分。这应该是基本的工作流程:
用户输入 -> 提交订单(或者如果可能,只提交 line_item) -> 返回更新后的 line_item 部分并更新计算等。
将来我想将此行为扩展到不再需要保存按钮的地步。
但是我不想重新渲染整个表单(对于快速用户输入来说太慢了),只是当前的 fields_for 部分。
问题在于,每当我将 js 响应限制为重新渲染当前 fields_for 部分时,form_for @order do |f| 中所需的 f 变量就会丢失。如果我在回复中替换整个表单,它就可以正常工作。
这是当前代码:
/views/orders/_form.html.slim
= nested_form_for @order do |f|
...
#line-items
= f.fields_for :line_items do |line_item|
= render "orders/line_item_fields", f: line_item
...
views/orders/_line_item_fields.html.slim
...
= f.label :product_id, "Product"
= f.collection_select(:product_id, Product.order(:name), :id, :name, class: "order_line_items_product_id submittable" )
= f.label :quantity
= f.text_field :quantity, class: "submittable"
= f.label :price
= f.text_field :price, class: "submittable"
...
.totals
p
span = f.object.total_price
/app/assets/javascripts/orders.js.erb
$('body').on('change', '.submittable', function () {
var current_item = $(this).closest('section')
var order_id = $('form.edit_order').data('order-id')
var data = $('form.edit_order').serialize();
current_item.addClass('item-to-refresh')
$.ajax({
url: "/orders/" + order_id,
data: data,
dataType: "script",
type: "patch",
error: function(response) {
response
},
success: function(response){
response
}
});
}
/app/controllers/orders_controller.rb
...
def update
@order = Order.find(params[:id])
authorize @order
if @order.update_attributes(order_params)
respond_to do |format|
format.html do
redirect_to order_path
flash[:success] = "Order #{@order.id} saved"
end
format.js
end
else
render 'edit'
end
end
...
/app/views/orders/update.js.erb
$('.item-to-refresh').replaceWith("<%= j render 'orders/line_item_fields' %>")
如上所述,响应总是带有错误ActionView::Template::Error (undefined local variable or method 'f' for #class... - 我知道它需要 f 变量,但我该如何传递它?我尝试将f: f、f: line_item 和主题上的类似变体添加到 update.js.erb 的渲染函数中,但没有成功。当然,如果我不尝试在响应中呈现表单元素,而是一个简单的<p>It works!</p>
另外,我在这里想到的一般概念是处理网络应用程序交互性的好方法吗?这仍然是一个老式的基于文档/页面的应用程序,重新加载页面就可以了。但是对于应用程序的某些部分,用户只希望获得即时反馈和结果——现在点击“保存”来获得一些计算和总计是不够的。 另一方面(除非上面的代码没有告诉你)我讨厌 Javascript 并且喜欢在解决这些问题时将其保持在最低限度。所以,我想找到一种可重用的方式来提交和刷新部分更改事件。
【问题讨论】:
标签: javascript ruby-on-rails ajax forms nested-forms