如何使用 Cocoon 以嵌套形式定位单个新输入项

How to target an individual new input item in nested form using Cocoon

我在呈现嵌套字段中的选项集合时遇到问题。该集合依赖于现有表单中的填写值。

我相信解决方法在于为每个新的嵌套输入字段指定一个具有唯一 ID 的新输入字段,但如果我错了请纠正我。

上下文

呈现订单时,程序会自动构建 'order_options' 的第一个字段(一个 table 连接订单和选项,给定其多对多关系)。该表单提供了一个字段供选择 bike_type,其中包含与特定自行车类型相关的选项。根据选择的 bike_type,该表单提供了一个带有选项的选择列表。我程序的这一部分正确呈现。

我的问题

当通过 Cocoon 的 link_to_add_association 添加另一个选项时,用户应该再次看到带有选项的相同选择列表,因为 bike_type 保持不变。此外,之前填写的选项应该保留(例如不会再次清空)。但是,在我现在的程序中

表格

<%= 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_id, collection: @bike_type_list, input_html:{
          value: @bike_type_list.object_id,
          id: "bike_type"
        }%>
      <% end %>

      <%= f.input :arrival %>
      <%= f.input :departure %>

      <%= order_bike.association :bike, collection: @bikes, input_html:{
        value: @bikes.object_id,
        id: "dynamic-bikes"
      } %>
  <% end %>

  <!-- test -->

  <%= f.simple_fields_for :order_options do |order_option| %>

      <%= render 'order_option_fields', f: order_option %>
  <% end %>

  <div>
      <%= link_to_add_association 'add option', f, :order_options, id:'add-option' %>
  </div>
  <!-- test -->

  <%= f.submit %>
<% end %>

JavaScript/Ajax

<script>
  // Dynamic bikes
  $(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) {
        console.log(response);
        // Dynamic bikes
        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 options after 1st build
        var options = response["options"];
        $("#dynamic-options").empty();

        $("#dynamic-options").append('<option></option>');
        for(var i=0; i< options.length; i++){
          $("#dynamic-options").append('<option value="' + options[i]["id"] + '">' + options[i]["name"] + '</option>');
        }
      }
    });
  });

  // Test dynamic options for nested form
  $(document).on('click', '#add-option', function(){
    console.log('test')
    var bike_type = $('#bike_type').val();
    console.log(bike_type)

    $.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) {
        console.log(response);
        var options = response["options"];
        // $("#dynamic-options").empty();

        $("#dynamic-options").append('<option></option>');
        for(var i=0; i< options.length; i++){
          $("#dynamic-options").append('<option value="' + options[i]["id"] + '">' + options[i]["name"] + '</option>');
        }
      }
    });
  });

</script>

嵌套形式

<div class="nested-fields">
  <%= f.association :option, collection: @options, input_html:{
        value: @options.object_id,
        id: "dynamic-options",
      } %>
</div>

订单控制器

class OrdersController < ApplicationController
  # skip_before_action :authenticate_user!

  def new
    @bike_store = Bike_store.find(params[:bike_store_id])
    @order = Order.new
    @order.order_bikes.build
    @order.order_options.build
    @bike_type_list = @bike_store.bike_types
    @bikes = []
    @options = []

    # Display bikes/options for type
    if params[:bike_type].present?
      @bikes = BikeType.find(params[:bike_type]).bikes
      @options = BikeType.find(params[:bike_type]).options
    end
    if request.xhr?
      respond_to do |format|
        format.json {
        render json: {bikes: @bikes, options: @options}
      }
    end
  end

    authorize @order
  end

  def create
    @order = Order.new(order_params)
    @bike_store = Bike_store.find(params[:bike_store_id])
    @order.bike_store = @bike_store
    authorize @order
    @order.save
    redirect_to root_path
  end


    private
  def order_params
    params.require(:order).permit(:arrival, :departure,
      order_bikes_attributes: [:id, :bike_id, :bike_quantity, :_destroy,
        bikes_attributes: [:id,:name, :bike_type_id,
          bike_types_attributes: [:id, :name]]],
      order_options_attributes: [:id, :option_id, :option_quantity, :_destroy,
        options_attributes: [:id, :name, :bike_type_id, :description,
          bike_types_attributes:[:id, :name]]])
          # )
  end
end

回调我也试过:

  // Dynamic options for nest form
  $(document).on('cocoon:after-insert', function(){
    //$(document).on('cocoon:after-insert', function(){
    console.log('test')
    var bike_type = $('#bike_type').val();
    console.log(bike_type)

    $.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) {
        console.log(response);
        var options = response["options"];
        // $("#dynamic-options").empty();

        $("#dynamic-options").append('<option></option>');
        for(var i=0; i< options.length; i++){
          $("#dynamic-options").append('<option value="' + options[i]["id"] + '">' + options[i]["name"] + '</option>');
        }
      }
    });
  });

我通过为属于 bike_type 的所有选项实施 object_id 来解决它。随后,我在 added option 上应用了 'cocoon:after-insert'

请在下面找到解决方案的代码。

_order_option_fields.html.erb

<div class="nested-fields row">
          <div class="col col-sm-5"> <%= f.association :option, collection: @options, input_html:{
          value: @all_options.object_id,
          id: "dynamic-options"
          }%> </div>

  <div class="col col-sm-5"><%= f.input :option_quantity %></div>
  <%= link_to_remove_association f do %>
      <i class="fas fa-trash"></i>
    <% end %>
</div>

脚本

<script >
    $(document).on('cocoon:after-insert', function(e, added_option_form){
      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 options = response["options"];
      $(added_option_form.find("#dynamic-options")).html("");
      $(added_option_form.find("#dynamic-options")).empty();
       $(added_option_form.find("#dynamic-options")).append('<option>Select option</option>');
      // console.log(options.length);
      for(var i=0; i< options.length; i++){
        $(added_option_form.find("#dynamic-options")).append('<option value="' + options[i]["id"] + '">' + options[i]["name"] + '</option>');
      }
    }
  });
  });