【问题标题】:Submitting a form and refreshing a fields_for partial via Ajax - undefined local variable f in response通过 Ajax 提交表单并刷新 fields_for 部分 - 未定义的局部变量 f 作为响应
【发布时间】: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: ff: line_item 和主题上的类似变体添加到 update.js.erb 的渲染函数中,但没有成功。当然,如果我不尝试在响应中呈现表单元素,而是一个简单的&lt;p&gt;It works!&lt;/p&gt;

另外,我在这里想到的一般概念是处理网络应用程序交互性的好方法吗?这仍然是一个老式的基于文档/页面的应用程序,重新加载页面就可以了。但是对于应用程序的某些部分,用户只希望获得即时反馈和结果——现在点击“保存”来获得一些计算和总计是不够的。 另一方面(除非上面的代码没有告诉你)我讨厌 Javascript 并且喜欢在解决这些问题时将其保持在最低限度。所以,我想找到一种可重用的方式来提交和刷新部分更改事件。

【问题讨论】:

    标签: javascript ruby-on-rails ajax forms nested-forms


    【解决方案1】:

    试试这个

    = render "orders/line_item_fields", :locals => {f: line_item}
    

    = render partial: "orders/line_item_fields", :locals => {f: line_item}
    

    【讨论】:

    • 同样的结果,我已经尝试过render "orders/line_item_fields", f: line_item,这是您代码的简写,不是吗?是否应该在控制器操作中使用这些变量?因为我不确定他们会从哪里来。
    【解决方案2】:

    我知道这有点晚了,但我遇到了同样的问题,我想在更新记录后重新渲染 fields_for 表单而不渲染完整的表单。

    我的解决方案是更改每个输入(选择、复选框等)字段的 id 和 name 属性,这些字段由 javascript 在更新后使用正确的 id 和名称动态创建(并在数据库中创建记录)。最后,动态字段看起来像是由表单构建器为编辑字段创建的字段。

    除了更改 id 和 name 属性之外,它还需要在 fields_for 表单元素之后添加一个额外的输入 type=hidden 字段。这也需要正确的 id 和 name 语法,如表单中的字段,并且 value 属性必须是新的记录 id。

    希望这会有所帮助!

    【讨论】:

      猜你喜欢
      • 2011-05-14
      • 1970-01-01
      • 2011-03-11
      • 2016-01-10
      • 1970-01-01
      • 2016-07-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多