6
votes

I have the following params and cannot get the strong parameters to work.

Here's my basic code, runnable in the Rails console for simplicity:

json = {
  id: 1,
  answers_attributes: {
    c1: { id: "", content: "Hi" },
    c2: { id: "", content: "Ho" }
  }
}

params = ActionController::Parameters.new(json)

Everything I've read says the following should work, but it only gives me the id and an empty hash of answers_attributes:

params.permit(:id, answers_attributes: [:id, :content])
=> { "id"=>1, "answers_attributes"=>{} }

If I instead manually list c1 and c2 (like below) it works, but this is really stupid because I don't know how many answers the user will supply, and this is a lot of duplication:

params.permit(:id, answers_attributes: { c1: [:id, :content], c2: [:id, :content] })
=> {"id"=>1, "answers_attributes"=>{"c1"=>{"id"=>"", "content"=>"Hi"}, "c2"=>{"id"=>"", "content"=>"Ho"}}}

I've tried replacing c1 and c2 with 0 and 1, but I still have to manually supply the 0 and 1 in my permit statement.

How can I permit an unknown length array of nested attributes?

2

2 Answers

5
votes

It's done with syntax like:

answers_attributes: [:id, :content]

The problem is the keys you are using in the answers_attributes. They are expected to be the ids of the answers_attributes or in the case of new records 0.

Changing these gives your expected outcome:

json = {
  id: 1,
  answers_attributes: {
    "1": { id: "", content: "Hi" },
    "2": { id: "", content: "Ho" }
  }
}

params = ActionController::Parameters.new(json)

params.permit(:id, answers_attributes:[:id, :content])
=>  {"id"=>1, "answers_attributes"=>{"1"=>{"id"=>"", "content"=>"Hi"}, "2"=>{"id"=>"", "content"=>"Ho"}}}

Edit: It appears that 0 is not the only key, I mean what if you have two new records. I use nested_form and it appears to use a very long random number.

3
votes

Your answers_attributes contains c1 and c2 which are not permitted. http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

WAY 1: You can pass the nested attributes as array of hashes.

json = {
  id: 1,
  answers_attributes: [ { id: "", content: "Hi" }, { id: "", content: "Ho" } ]
}

Now params.permit(:id, answers_attributes: [:id, :content]) gives

{"id"=>1, "answers_attributes"=>[{"id"=>"", "content"=>"Hi"}, {"id"=>"", "content"=>"Ho"}]}

WAY 2: You can pass as hash of hashes like

json = {
  id: 1,
  answers_attributes: {
    c1: { id: "", content: "Hi" },
    c2: { id: "", content: "Ho" }
  }
}

Both WAY 1 and WAY 2 have the same effect in the model level. But permit doesn't allow the values to pass unless the keys are explicitly specified. So c1 and c2 will not be permitted unless explicitly specified like

params.permit(:id, answers_attributes: [c1: [:id, :content], c2: [:id, :content]])

which is really a pain in the ass.