Search code examples
ruby-on-railsnested-forms

editing multiple child records while accessing values of other attributes of said record


For class

class Product
  has_many :productunits
  accepts_nested_attributes_for :productunits

class Productunit 
  belongs_to :product
  belongs_to :unit

  validates :product_id, presence: true
  validates :unit_id, presence: true

the following form is meant only to update existing records (of what is effectively a join table), while formatting the fields in columns (one column per child) and effecting some view logic on whether the field should be shown or not.

<div class='grid-x grid-margin-x'>
  <%= f.fields_for :productunits do |price_fields| %>
    <div class='cell small-2 text-right'>
      <h4><%# productunit.unit.name %> </h4>
      <%# if productunit.unit.capacity == 2 %
      2 <%= label %> <%= price_fields.number_field :price2 %>
      <%# end %>
    </div>
  <% end %>
</div>

However a number problems are arising:

  1. I cannot invoke the value of an attribute of record being edited (say productunit.unit.capacity)
  2. The natural break in child records is not accessible to html tags for formatting (<div class='cell [...]). Worse, rails is throwing the child record id outside the div definition </div> <input type="hidden" value="3" name="product[productunits_attributes][1][id]" id="product_productunits_attributes_1_id" /> <div class='cell small-2 text-right'>
  3. submitting the form returns an error Productunits unit can't be blankwhich would be fair for a new record, but is definitely not expected when editing an existing one.

Unfortunately, the rails guide is thin in this regard.


Solution

    1. I cannot invoke the value of an attribute of record being edited

    You can get the object wrapped by a form builder or input builder though the object method:

    <div class='grid-x grid-margin-x'>
      <%= f.fields_for :productunits do |price_fields| %>
        <div class='cell small-2 text-right'>
          <h4><%= price_fields.object.unit.name %> </h4>
          <% if price_fields.object.unit.capacity == 2 %
          2 <%= price_fields.label :price2 %> <%= price_fields.number_field :price2 %>
    
          <% end %>
        </div>
      <% end %>      
    </div>
    

    2 The natural break in child records is not accessible to html tags for formatting...

    fields_for just iterates through the child records. Its just a loop. I'm guessing you just have broken html like a stray </div> tag or whatever you're doing with <%= label %>.

    submitting the form returns an error Productunits unit can't be blankwhich would be fair for a new record, but is definitely not expected when editing an existing one.

    You're not passing a id for the unit. Rails does not do this automatically. Either use a hidden input or the collection helpers.

    <div class='grid-x grid-margin-x'>
      <%= f.fields_for :productunits do |price_fields| %>
        <div class='cell small-2 text-right'>
          <h4><%= price_fields.object.unit.name %> </h4>
          <% if price_fields.object.unit.capacity == 2 %
          2 <%= price_fields.label :price2 %> <%= price_fields.number_field :price2 %>
          <% end %>
          <%= price_fields.collection_select(:unit_id, Unit.all, :id, :name) %> 
          # or
          <%= price_fields.hidden_field(:unit_id) %> 
        </div>
      <% end %>      
    </div>
    

    On a side note you should name your model ProductUnit, your table product_units and use product_unit everywhere else. See the Ruby Style Guide.