I'm attempting to created a deeply nested form with dynamically generated content using the examples laid out in this question. However when I try to do it more than one level deep, I run into issues because the content generation happens more than once in the first generated form and causes nothing to happen when I try to add fields from the second level of generated content.
simplified model layout,
Task
has_one VendorUpload
has_many VendorShippingLogs
has_many VendorShippingLogProducts
the Vendor Shipping Log Products is where I'm stuck because that form is attempting to generate its content within the Vendor Shipping Log content that is itself dynamically generated and I'm unsure how to code it into its own Helper. Attached code,
Models -->
class Task < ApplicationRecord
has_one :vendor_upload, :dependent => :destroy
has_many :vendor_shipping_logs, :through => :vendor_upload
has_many :vendor_shipping_log_products, :through => :vendor_shipping_logs
accepts_nested_attributes_for :vendor_upload, :reject_if => :upload_type_blank, :allow_destroy => true
end
class VendorUpload < ApplicationRecord
belongs_to :task
has_many :vendor_shipping_logs, inverse_of: :vendor_upload, :dependent => :destroy
has_many :vendor_shipping_log_products, :through => :vendor_shipping_logs
accepts_nested_attributes_for :vendor_shipping_logs
end
class VendorShippingLog < ApplicationRecord
belongs_to :vendor_upload
has_many :vendor_shipping_log_products, inverse_of: :vendor_shipping_log, :dependent => :destroy
accepts_nested_attributes_for :vendor_shipping_log_products
end
class VendorShippingLogProduct < ApplicationRecord
belongs_to :vendor_shipping_log
end
tasks_controller.rb
def new
@task = Task.new
@task.build_vendor_upload.vendor_shipping_logs.build.vendor_shipping_log_products.build
end
private
def task_params
params.require(:task).permit!
end
application_helper.rb -->
def new_child_fields_template(form_builder, association, options = {})
options[:object] ||= form_builder.object.class.reflect_on_association(association).klass.new
options[:partial] ||= association.to_s.singularize
options[:form_builder_local] ||= :f
content_for :jstemplates do
content_tag(:div, :id => "#{association}_fields_template", :style => "display: none") do
form_builder.fields_for(association, options[:object], :child_index => "new_#{association}") do |f|
render(:partial => options[:partial], :locals => { options[:form_builder_local] => f })
end
end
end
end
def add_child_link(name, association)
link_to(name, "javascript:void(0)", :class => "add_child btn btn-success btn-sm fa fa-plus-circle", :style => "margin-left: 15px;margin-top: 15px;", :"data-association" => association)
end
def remove_child_link(name, f)
f.hidden_field(:_destroy) + link_to(name, "javascript:void(0)", :style => "height: 30px; margin-left: 5px;", :class => "remove_child btn btn-danger fa fa-close")
end
new.html.erb -->
<%= form_for @task, url: tasks_path, method: :post, :html => {:multipart => true } do |f| %>
<%= f.fields_for :vendor_upload do |ff| %>
<%= ff.fields_for :vendor_shipping_logs do |fff| %>
<%= render "vendor_shipping_log", :f => fff %>
<% end %>
<p><%= add_child_link " Add Customer", :vendor_shipping_logs %></p>
<%= new_child_fields_template f, :vendor_shipping_logs %>
<% end %>
<% end %>
<div id="jstemplates">
<%= yield :jstemplates %>
</div>
_vendor_shipping_log.html.erb -->
<%= f.fields_for :vendor_shipping_log_products do |ff| %>
<%= render "vendor_shipping_log_product", :f => ff %>
<% end %>
<%= new_child_fields_template f, :vendor_shipping_log_products %>
<p><%= add_child_link " Add Product", :vendor_shipping_log_products %></p>
<%= remove_child_link "", f %>
_vendor_shipping_log_product.html.erb -->
<div class="fields">
<div class="form-group form-group-sm">
<%= f.label :item_id, "Item ID:", class: 'col-sm-3 control-label' %>
<div class="col-sm-9">
<div style="display: inline-flex;">
<%= f.text_field :item_id, class: "form-control" %>
</div>
</div>
</div>
<%= remove_child_link "", f %>
</div>
application.js -->
$(function() {
$('a.add_child').click(function() {
var association = $(this).attr('data-association');
var template = $('#' + association + '_fields_template').html();
var regexp = new RegExp('new_' + association, 'g');
var new_id = new Date().getTime();
$(this).parent().before(template.replace(regexp, new_id));
$()
return false;
});
});
$(function(){
$(document).on("click", 'a.remove_child', function() {
var hidden_field = $(this).prev('input[type=hidden]')[0];
if(hidden_field) {
hidden_field.value = '1';
}
$(this).parents('.fields').hide();
return false;
});
});
The code here basically mimics everything from the example and works fine for one level deep nested attributes, but anything beyond that simply doesn't work. I don't get any feedback when trying to click the "Add Product" button from the code above.
I hope I laid out everything here clearly enough as it's a difficult problem to explain as a novice. Would appreciate any points in the right direction!