1

I've looked through every post I can imagine regarding this, but can't come across a solution. I have a Task with a nested resource of Task Products which grabs its ID's from another database. There can be multiple Task Products nested in a Task, and each one is dynamically created with some Javascript and an Application Helper (both of which are purely a copy/paste on my part).

I built up the code for these nested parameters with the help of Railscast 196/197 and this post. All the relevant code is below, and then onto the problem:

task.rb:

class Task < ApplicationRecord has_many :task_products, :dependent => :destroy accepts_nested_attributes_for :task_products, :reject_if => lambda { |p| p.values.all?(&:blank?) }, :allow_destroy => true end 

task_controller.rb:

class TasksController < ApplicationController def new @task = Task.new @task.task_products.build end def task_params params.require(:task).permit(:id, :task_name, . . ., :created_at, :updated_at, :task_products_attributes => [:task_id, :product_id, :created_at, :updated_at]) end 

application_helper.rb:

module ApplicationHelper 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_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 

application.js:

$(function() { $('form 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; }); }); 

_form.html.erb:

<%= form_for @task, url: tasks_path, method: :post, :html => {:multipart => true } do |f| %> <%= f.fields_for :task_products, TaskProduct.new do |builder| %> <%= render "task_product", :f => builder %> <% end %> <p><%= add_child_link "Add Product", :task_products %></p> <%= new_child_fields_template f, :task_products %> 

_task_product.html.erb:

<div class="fields"> <div class="form-group"> <%= f.label :product_id, "Product:", class: 'col-sm-3 control-label' %> <%= f.collection_select(:product_id, @items, :id, :item_select, {:include_blank => 'Select Item...'}, {:class => 'form-control'}) %> <%= remove_child_link "Remove Product", f %> </div> </div> 

Code in console after submit along with the error it produced:

Parameters: {"utf8"=>"✓", "authenticity_token"=>"KDQ...8bxur2A==", "task"=>{"task_type"=>"Store Order", "task_products_attributes"=>{ "0"=>{"product_id"=>"1", "_destroy"=>"false"}, "1484792726712"=>{"product_id"=>"2", "_destroy"=>"false"}, "new_task_products"=>{"product_id"=>"", "_destroy"=>"false"}}, "initiated_by"=>"1", "initiated_for"=>"5", "active"=>"true", "status"=>"Pending Acceptance"}, "commit"=>"Create Task"} Unpermitted parameters: 0, 1484792726712, new_task_products 

Generated HTML examples of error producing code of multiple generated selects:

<select class="form-control" name="task[task_products_attributes][0][product_id]" id="task_task_products_attributes_0_product_id"> <select class="form-control" name="task[task_products_attributes][1484794199756][product_id]" id="task_task_products_attributes_1484794199756_product_id"> 

and its template, which I thought should be throwing back ' "_destroy"=>true' in the console?:

<select class="form-control" name="task[task_products_attributes][new_task_products][product_id]" id="task_task_products_attributes_new_task_products_product_id"> 

The problem, as seen above, is that my generated Task Product form is adding an additional bracketed identifier(child index?) into my select box for each item, and I don't know how to dive into the Application Helper to remove it, or move it where it belongs. Or does it actually belong there and I'm doing something else wrong? I get the feeling that each individual Task Product should have it's own unique identifier, but Rails isn't having any of that and says its unpermitted.

The 'new_child_fields_template' in the Application Helper is a bit over my head, and while I understand how it's getting the unique identifier to inject into my Task Product form, I don't know how it's throwing it into the "name=" field of my select that is apparently screwing everything up. Anybody have some guidance on how I can overcome this problem? Thanks!

6
  • I believe I may of had a similar problem with the hidden field you are talking about. CTRL+F and search for include_hidden on this page: http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html. You can use that as a param in the #collection_check_boxes. = collection_check_boxes(:object, :field, [opts], :method1, :method2, {include_hidden: false}, ... &block Commented Jan 19, 2017 at 4:22
  • See if this helps how to permit an array with strong parameters Commented Jan 19, 2017 at 4:33
  • @maxple, thank you for the link (it'll be useful later!) but I don't think this is what I'm trying to accomplish. The generated code from the Application Helper is putting an additional value into the "name" field of the select dropdown, and that is trying to be passed as a value for the form. I don't need it to be passed as a value. All I need to do is make sure the Task Product is linked to the Task it is a part of (which I think happens automatically), so all I think I need to do is figure out how to get rid of the second bracketed item in the name column. Commented Jan 19, 2017 at 5:01
  • @Shiyason, thank you for the response, but this isn't a problem with hidden fields. Just for kicks, I added the "include_hidden" clause though, and it didn't change the Unpermitted error. Commented Jan 19, 2017 at 5:02
  • What do you mean by "which grabs its ID's from another database" do you really mean another DB or another table? Commented Jan 19, 2017 at 6:52

2 Answers 2

3

For nested attributes to work nicely with strong attributes, you need to pass the id and _destroy attributes to the list of attributes. In your tasks_controller.rb:

params.require(:task).permit( task_products_attributes: [ :id, :product_id, :created_at, :updated_at, :_destroy ] ) 

For a clearer explanation you can go to this answer.

Sign up to request clarification or add additional context in comments.

4 Comments

You could use dinamically generated fields, so you don't risk forgetting any of it like this: task_products_attributes: ([:_destroy] + TaskProducts.column_names.map(&:to_sym))
@zamakkat then I'm really confused because I've done exactly this, and the post you linked describes the exact same problem I'm having, but the "Unpermitted Parameters" problem persists. This is frustrating beyond belief, because I have no idea what's keeping this from working, I've looked at every possible solution I can think of.
Clarification: This is what my params.require is now, and it's still throwing the "Unpermitted" errors for the "0"=>, etc. parameter:: task_products_attributes: [:product_id, :task_id, :created_at, :updated_at, :id, :_destroy] /////////////// "task_products_attributes"=>{"0"=>{"product_id"=>"2", "_destroy"=>"false"}, "1484881558666"=>{"product_id"=>"3", "_destroy"=>"false"}, "new_task_products"=>{"product_id"=>"", "_destroy"=>"false"}}, . . . Unpermitted parameters: 0, 1484881558666, new_task_products
Why do you have both task_id and product_id in the params? Could you move the id to the begging so your params look something like this: :id, :product_id, :_destroy. See if that works...
0

One final comment, unless somebody has any additional input as to why including ":id" in my nested parameters isn't working.

I finally got it working by changing the permitted parameters to

params.require(:task).permit! 

This apparently is a blanket permit for anything that happens to fall into the :task parameter, whether it's nested or not. I gather that this isn't recommended, but the site it's for is closed off to very few team members, so I assume this shouldn't be an issue? Additionally, I am still looking for insight into why the template for "new_task_products" isn't giving me a "destroy=>1" like it should be since the product_id is blank. Is this code not correct?

accepts_nested_attributes_for :task_products, :reject_if => lambda { |p| p.values.all?(&:blank?) }, :allow_destroy => true 

I might try to move the template to a somewhere outside of the view though to avoid the issue. Appreciate the help everyone!

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.