Search code examples
ruby-on-railsassociationsnested-attributes

Listings and Updating Associated IDs - Rails 4, Form_for


I have a model called Foo, which has many bars. I want to be able to add / remove foo.bars by adding or removing associated foo.bars.ids. How can I achieve this?

I tried:

Foo#edit

def edit
  @bars = @foo.bars
end

Form

<%= form_for @foo, url: edit_foo_path(@foo.id) do |p| %>
  ...
  <%= p.text_area @bars %> #<= returns undefined method `Collection..' for `foo`
  ...
  <%= p.fields_for @foo.bars do |b| %>
    <%= b.text_area :ids %> #<= returns in 'unpermitted param 'bar' on #update' 
  <% end %>
  ...
<% end %>

I'm thinking fields_for would not work here anyway since I am not updating bar's attributes. Instead, I want to be able to add / remove foo.bars by listings their ids


Solution

  • Hook into the Foo#bar_ids and Foo#bar_ids= methods which the has_many association gives you. You could do this via checkboxes for example.

    <% form_for @foo do |f| %>
      <% @bars.each do |bar| %>
        <div class="bar--checkbox">
          <%= check_box_tag "foo[bar_ids]", bar.id, @foo.bar_ids.include?(bar.id) %>
          <%= bar.name %>
        </div>
      <% end %>
      <%= f.submit "Save" %>
    <% end %>
    

    Now, in params you'll get something like

    params = {:id => 123, :foo => {:bar_ids => [1, 5, 8, 10]})
    

    and you can do the usual update controller code:

    @foo = Foo.where(:id => params[:id])
    @updated = @foo.update_attributes(params[:foo])
    

    which is like doing this:

    @foo.bar_ids = [1, 5, 8, 10]
    @foo.save
    

    NOTE - in your code you're setting @bars to be @foo's current bars. It could be all the bars in the system for example, which would be better in terms of adding and removing whichever bars you want.