svoop
svoop

Reputation: 3464

Nested form with simple_form and strong parameters

A nested has_many association with simple_form returns the following params hash:

params
# => {
       "user"=>{
         "first_name"=>"John",
         "last_name"=>"Doe",
         "bank_accounts_attributes"=>{
           "-1"=>{
             "_destroy"=>"0",
             "iban"=>"fakeiban",
             "bic"=>"fakebic"
           },
           "new-bank-account"=>{
             "_destroy"=>"0",
             "iban"=>"",
             "bic"=>""
           }
         }
       }
     }

The key "-1" is a new, not yet persisted bank_accout created by duplicating the "new-bank-account" template in the DOM (which is ignored since the iban is blank).

How do I permit these for strong parameters?

I've tried the following to no avail:

permitted_params = {
  :first_name,
  :last_name,
  { 
    :bank_accounts_attributes=>[:iban, :bic]
  }
}

params.require(:user).permit(*permitted_params)
Unpermitted parameters: -1, new-bank-account
# => {
       "user"=>{
         "first_name"=>"John",
         "last_name"=>"Doe",
         "bank_accounts_attributes"=>{}
       }
     }

What am I doing wrong here?

UPDATE:

The following works, but I don't want to include the negative keys (which stand for unpersisted relations) everywhere explicitly:

params.require(:user).permit(:first_name, :last_name,  bank_accounts_attributes: {"-1" => [:iban, :bic]})

UPDATE 2:

The problem appears to be the "new-bank-account" key:

p = ActionController::Parameters.new user: { first_name: "Foo", bank_accounts_attributes: {"-1" => {iban: 'xxx'}, "-2" => {iban: 'yyy'}}}
p.require(:user).permit(:first_name, bank_accounts_attributes: :iban)
# => {"first_name"=>"Foo", "bank_accounts_attributes"=>{"-1"=>{"iban"=>"xxx"}, "-2"=>{"iban"=>"yyy"}}}

p = ActionController::Parameters.new user: { first_name: "Foo", bank_accounts_attributes: {"-1" => {iban: 'xxx'}, "new-bank-account" => {iban: 'yyy'}}}
p.require(:user).permit(:first_name, bank_accounts_attributes: :iban)
# => {"first_name"=>"Foo", "bank_accounts_attributes"=>{}}

It appears I have to remove the template "new-bank-account" from the DOM before the form is submitted.

Upvotes: 0

Views: 790

Answers (1)

svoop
svoop

Reputation: 3464

I'll answer my own question:

Strong parameters accept has_many keys only if they are integers. One non-integer key is sufficient for all nested records to disappear from the params hash. There are thus two solutions:

  • Use a non-integer key such as "new-entry" for the template and make remove it from the DOM prior to form submission.
  • If you're like me and don't want this extra JS-code, use a reserved numeric key such as "999999" for the template and make sure new, unpersisted nested records don't clash. Since the keys of persisted records are "0", "1" etc, we use a counter which assigns "-1" and counts down. Another approach would be a timestamp e.g. Date.now() (fast, but doesn't work on <=IE8) or +new Date() (slower, but works on IE8).

Upvotes: 0

Related Questions