ruby-on-railsnested-attributescocoon-gem

Update multiple checkboxes on association model through nested attributes | Cocoon gem | Rails


I have a House model, which has_many house_rules

class House < ActiveRecord::Base
 has_many :house_rules, dependent: :destroy
 accepts_nested_attributes_for :house_rules, reject_if: :all_blank, allow_destroy: true
end

Here is the house_rule model

class HouseRule < ActiveRecord::Base
 belongs_to :house
 enum rule_type: { predefined: 0, user_defined: 1 }
 enum allowed: { no: 0, yes: 1 }
end

Here are the other columns of house_rules table

create_table "house_rules", force: :cascade do |t|
    t.integer  "house_id"
    t.string   "rule"
    t.integer  "rule_type",  default: 1
    t.integer  "allowed",    default: 0
    t.datetime "created_at",             null: false
    t.datetime "updated_at",             null: false
end

I have some predefined house_rules saved in database with allowed column set to 0 by default.For example:

<HouseRule id: 4, house_id: nil, rule: "Smoking allowed", rule_type: 0, allowed: 0, created_at: "2017-03-26 20:54:09", updated_at: "2017-03-26 20:54:09">

I want to have all these house_rules as check_boxes in the new house form. Like this. enter image description here

So, when users checks the checkbox with label 'Smoking allowed', a new record is created like this

<HouseRule id: 5, house_id: 1, rule: "Smoking allowed", rule_type: 1, allowed: 1, created_at: "2017-03-26 20:54:09", updated_at: "2017-03-26 20:54:09">

rule_type: 1 #user_defined rule
allowed: 1   #smoking is allowed

This is my new house form

<%= simple_form_for @house do |f| %>
<div id="house_rules">
  <%=  f.simple_fields_for :house_rules do |house_rule| %>
  <% HouseRule.predefined.each do |rule| %>
  <div>
    <%= house_rule.label :allowed, rule.rule %>
    <%= house_rule.check_box :allowed, {}, 'yes', 'no' %> 
  </div>
  <% end %>
  <% end %>
</div>
<% end %>

The above code shows the checkboxes as expected, but doesn't work as expected. Can someone guide me on how to make this work?

Also, I have whitelisted these in HousesController

house_rules_attributes: [:id, :allowed, :_destroy])

Edit:

This is the code the form produces for each checkbox

<label for="house_house_rules_attributes_0_allowed">Smoking Allowed</label>
 <input name="house[house_rules_attributes][0][allowed]" type="hidden" value="0"><input type="checkbox" value="1" name="house[house_rules_attributes][0][allowed]" id="house_house_rules_attributes_0_allowed">

The problem is, the id and name attributes are same for all the checkboxes, so even if I check a checkbox, only the first checkbox is always being checked.

A new house_rule record is being created for the associated house, but the allowed value is still 0, it's not 1.

This is my HousesController new action

def new
 @house = House.find(params[:id])
 @house.house_rules.build
end

Solution

  • This does not render the form correctly, since you are not actually iterating over nested rows, that is why the id is 0 for all rows.

    Instead I would precreate all predefined rules for a house in your controller, in the new action. As follows:

    def new 
      @house = House.new 
      HouseRule.predefined.each do |hr|
        @house.house_rules.build(rule: hr.rule)
      end 
    end 
    

    And in your view just iterate over the house-rules as usual:

    <%=  f.simple_fields_for :house_rules do |house_rule| %>
      <%= house_rule.label :allowed, house_rule.object.rule %>
      <%= house_rule.check_box :allowed, {}, 'yes', 'no' %> 
    <% end %>