如何创建不属于表/表单的字段影响选项选择的动态表单

Posted

技术标签:

【中文标题】如何创建不属于表/表单的字段影响选项选择的动态表单【英文标题】:How to create a dynamic form where fields not belonging to a table/form influence the option choices 【发布时间】:2020-02-14 01:33:20 【问题描述】:

我能够创建一个订单表单,用户可以在其中填写一个bike_type,然后表单会呈现属于这个bike_type 的自行车。

订单通过连接表链接到自行车(因为它们之间存在多对多关系)。 自行车属于bike_type bike_type 属于bike_store

问题

当表单重新呈现时,例如(i) 引发验证,因此再次呈现“新”操作或 (ii) 当我想编辑订单时:

所有字段都使用以前的数据重新填充,但 bike_type 和 Bike 字段除外(这些字段为空)。

代码

订单控制器

class OrdersController < ApplicationController
  def new
    @bike_store = BikeStore.find(params[:bike_store_id])
    @order = Order.new
    @order.order_bikes.build
    @bike_type_list = @bike_store.bike_types
    @bikes = []

    # Display bikes for category
    if params[:bike_type].present?
      @bikes = BikeType.find(params[:bike_type]).bikes
    end
    if request.xhr?
      respond_to do |format|
        format.json 
        render json: bikes: @bikes
      
      end
    end
    authorize @order
  end

  def create
    @order = Order.new(order_params)
    @bike_store = BikeStore.find(params[:bike_store_id])
    @order.bike_store = @bike_store
    authorize @order
    @order.save
    if @order.save
      redirect_to bike_store_path(@bike_store)
    else
      @bike_type_list = @bike_store.bike_types
      render 'new'
    end
  end

  def edit
    @bike_store = BikeStore.find(params[:bike_store_id])
    @order = Order.find(params[:id])
    @bike_type_list = @bike_store.bike_types
    @bikes = @order.bikes
    authorize @order
  end

  def update
    @bike_store = BikeStore.find(params[:bike_store_id])
    @order = Order.find(params[:id])
    @bikes = @order.bikes
    if @order.update(order_params)
      redirect_to redirect_to bike_store_path(@bike_store)
    else
      render 'edit'
    end
      authorize @order
  end

    private
  def set_order
    @order = Order.find(params[:id])
  end

  def order_params
    params.require(:order).permit(:arrival, :departure, :payment,
      order_bikes_attributes: [:id, :bike_id, :bike_quantity, :_destroy,
        bikes_attributes: [:id,:name, :bike_type_id,
          bike_types_attributes: [:id, :name]]])
  end
end

视图/订单/_form

<%= simple_form_for [@bike_store, @order] do |f|%>
  <%= f.simple_fields_for :order_bikes do |order_bike| %>
    <%= order_bike.simple_fields_for :bikes do |bike| %>
      <%= bike.input :bike_type, collection: @bike_type_list, input_html:
        id: "bike_type"
      %>
    <%= order_bike.association :bike, collection: @bikes, input_html:
      value: @bikes.object_id,
      id: "dynamic-bikes"
     %>
    <% end %>
  <% end %>
<% end %>

<script >
  // dynamic bikes for change category
  $(document).on("change", "#bike_type", function()
    var bike_type = $(this).val();

    $.ajax(
      url: "/bike_stores/<%= @bike_store.id %>/orders/new",
      method: "GET",
      dataType: "json",
      data: bike_type: bike_type,
      error: function (xhr, status, error) 
        console.error('AJAX Error: ' + status + error);
      ,
      success: function (response) 
      var bikes = response["bikes"];
      $("#dynamic-bikes").empty();

      $("#dynamic-bikes").append('<option>Select bike</option>');
      for(var i=0; i< bikes.length; i++)
        $("#dynamic-bikes").append('<option value="' + bikes[i]["id"] + '">' + bikes[i]["name"] + '</option>');
      
    
  );
  );
  // dynamic bikes for releading form (e.g. new)
   $(document).ready(function()
    var bike_type = $("#bike_type").val();

    $.ajax(
      url: "/bike_stores/<%= @bike_store.id %>/orders/new",
      method: "GET",
      dataType: "json",
      data: bike_type: bike_type,
      error: function (xhr, status, error) 
        console.error('AJAX Error: ' + status + error);
      ,
      success: function (response) 

      var bikes = response["bikes"];
      $("#dynamic-bikes").empty();

      $("#dynamic-bikes").append('<option>Select bike</option>');
      for(var i=0; i< bikes.length; i++)
        $("#dynamic-bikes").append('<option value="' + bikes[i]["id"] + '">' + bikes[i]["name"] + '</option>');
      
    
  );
  );
</script>

【问题讨论】:

用户没有理由传递那么多信息。尤其是嵌套属性。用户应该只传递指向标准化产品表的 id 及其数量。我认为您基本上只是在这里给自己一个巨大的质量分配漏洞。 @Max,这是一个有趣的观点,因为我目前正在做的事情似乎确实过于复杂和脆弱。由于这是我第一次在类似电子商务的环境中工作,我将提出一个新问题,其中包含更多概念性问题,如何解决处理订单、bike_types 和相关自行车的问题。 【参考方案1】:

听起来您想将表单提交前的表单页面的“状态”保留到提交后的同一表单页面中(发生验证错误时)。通常,您只需要“重播”上一页中发生的 Javascript 事件,因此“通常”,下面的内容应该已经足够让它工作了:

TL;DR:

app/views/order/_form.html.erb:

<!-- ... -->
<!-- ... -->
<script>
  // on document ready (after all DOM elements have been loaded):
  $(function() 
    // replay event by immediately triggering a
    // "change" event on your `#bike_type` input
    $('#bike_type').change();
  )

  // dynamic bikes for change category
  $(document).on("change", "#bike_type", function()
    // ...
    // ...
  )

  // ...
  // ...
</script>

【讨论】:

这并不总是有效,特别是如果您有意不想在页面加载后立即更改trigger。如果它不能完全正常工作,请告诉我上面代码的其他影响,如果它很容易修复,我会更新上面的答案。 非常感谢您的详尽解释。澄清一下,您建议:(i) 保留“更改”事件 (ii) 通过在最后一行之前添加 $('#bike_type').change(); 来更改 $(document).ready(function() @techquestion ... 表示您的其余代码;所以 i) 是正确的,并且 ii) 您可以在 &lt;script&gt; 的最顶层上下文中的任何位置添加 $(document).ready(function() ... ),因为无论您将其放在何处(之前或之后),无论如何,DOMContentLoaded 事件都是异步发生的 感谢您的上下文!它似乎不起作用,但我正在考虑为我的订单采用一种新方法,因为这似乎太复杂而无法正常运行。

以上是关于如何创建不属于表/表单的字段影响选项选择的动态表单的主要内容,如果未能解决你的问题,请参考以下文章

如何在Access中使用子表单时编辑现有记录而不创建新记录?

如何限制表单集中的字段选择?

如何根据表单输入添加动态复选框?

根据下拉选择创建动态下拉选项 - 卡住

选择下拉选项时在表单中添加输入字段

应用过滤器时动态更新所选选项?