Jump to content

The ultimate community for Ruby on Rails developers.


Photo

:has_many :through with checkboxes and additional fields


  • Please log in to reply
2 replies to this topic

#1 Simply Misunderstood

Simply Misunderstood

    Passenger

  • Members
  • 5 posts

Posted 05 February 2014 - 04:15 PM

I can not seem to get this concept wrapped around my head.  I have a 3 models; a product model, an option model and then a product_options model that joins the two together.  The product_options table has an additional field called "cost".

 

The models are established like so:

Product.rb
has_many :product_options
has_many :options through: :product_options

accepts_nested_attributes_for :product_options, :reject_if => proc { |att| att.blank? }, :allow_destroy => true
Option.rb
has_many :product_options
has_many :products through: :product_options
ProductOption.rb
belongs_to :product
belongs_to :option

In my Product creation (and update) form I am trying to assign Options to my product using checkboxes.  I also want to pass the cost at the same time.  But I can not seem to get this to work.

 

How do I write the form so that I can have a list of all the options, with checkboxes and a text input field next to each.  Then I can check the checkbox for the options I want to associate with and fill in the text box for cost.  So that when I submit the correct options will be added to product_options with the cost column being populated with the correct information?

 

I scoured the web and can not find a straightforward answer.  There are plenty of responses from 2008 that are quite ugly and look hackish(sp?).  I keep thinking rails has to have a clean way of doing this.

 

My last attempt looks like this:

<%= f.fields_for :product_options do |po| %>
  <span class="ul">
    <% Option.all.each do |option| %>
      <label class="li">
        <%= pm.check_box :option %> 
        <%= option.name %> - <%= pm.text_field :cost %>
      </label><br />
    <% end %>
  </span>
<% end %>

It looks like it works, except this is what gets passed to the server (via parameters) no matter what I put in the text box.

"product_options"=>{"option"=>"0", "cost"=>""}},

For example, I checked the option with id #2, and put a cost of 1.00

 

I even tried this, and while it works for the checkboxes and appropriately creates the association it does not accept additional information (such as the cost column).

<%= check_box_tag :product_option_ids, option.id, @product.product_options.include?(option), :name => 'product[product_option_ids][]' %> 


#2 Simply Misunderstood

Simply Misunderstood

    Passenger

  • Members
  • 5 posts

Posted 05 February 2014 - 11:27 PM

Is this more difficult than I assume?



#3 Simply Misunderstood

Simply Misunderstood

    Passenger

  • Members
  • 5 posts

Posted 10 March 2014 - 04:20 PM   Best Answer

This was a lot easier then I was making it out to be.
 
In my controller I create the product_options.build methods for each option in the Options table like so:
 
 

     def create_options
      # product_options
      Option.active.each do |obj|
        if !@product.option_ids.include?(obj.id)
          @product.product_options.build(:option_id => obj.id)
        end
      end
     end

 
 
Then in my view, I display that list of checkboxes like so:
 
 

        <%= f.fields_for :product_options do |ff| %>
          <span class="ul">
              <label class="li">
                <%= ff.check_box :_destroy, {:checked => ff.object.persisted?}, '0', '1' %> 
                (note, I flipped the checkbox.  Checked == false and unchecked == true for _destroy method)
                <%= ff.hidden_field :option_id %> 
                <%= ff.object.option.name %> - <%= ff.text_field :cost %>
              </label><br />
          </span>
        <% end %>

 
 
My model stayed pretty much the same:
 
 

accepts_nested_attributes_for :product_options,       reject_if: proc { |att| att['option_id'].to_i == 0 },  allow_destroy: true

 
Then the options that are checked, are saved in the database automatically and atomically when product.save is called (no magic necessary in the create/update/destroy action).
 
Short, sweet and straight to the point.


  • Jamie likes this




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users