如何使用 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 保持不变。此外,之前填写的选项应该保留(例如不会再次清空)。但是,在我现在的程序中
- 之前填写的代码被删除(因为我需要清空选择列表以防bike_type发生变化)
- 新添加的嵌套表单不包含任何选项(我假设,但我不确定,因为它只对第一个表单字段使用 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>');
}
}
});
});
我在呈现嵌套字段中的选项集合时遇到问题。该集合依赖于现有表单中的填写值。
我相信解决方法在于为每个新的嵌套输入字段指定一个具有唯一 ID 的新输入字段,但如果我错了请纠正我。
上下文
呈现订单时,程序会自动构建 'order_options' 的第一个字段(一个 table 连接订单和选项,给定其多对多关系)。该表单提供了一个字段供选择 bike_type,其中包含与特定自行车类型相关的选项。根据选择的 bike_type,该表单提供了一个带有选项的选择列表。我程序的这一部分正确呈现。
我的问题
当通过 Cocoon 的 link_to_add_association 添加另一个选项时,用户应该再次看到带有选项的相同选择列表,因为 bike_type 保持不变。此外,之前填写的选项应该保留(例如不会再次清空)。但是,在我现在的程序中
- 之前填写的代码被删除(因为我需要清空选择列表以防bike_type发生变化)
- 新添加的嵌套表单不包含任何选项(我假设,但我不确定,因为它只对第一个表单字段使用 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>');
}
}
});
});