2

So I have a similar situation going on as one of the users that posted here Rails 4 - checkboxes for has_and_belongs_to_many association, where this would be a perfect example:

#app/models/campaign.rb
class Campaign < ActiveRecord::Base
  has_and_belongs_to_many :options
end

and then

#app/models/option.rb
class Option < ActiveRecord::Base
  has_and_belongs_to_many :campaigns
end

Here's the way my join table migration look:

class CreateJoinTableCampaignsOptions < ActiveRecord::Migration[5.1]
  def change
    create_join_table :campaigns, :options do |t|
      t.index [:campaign_id, :option_id], :unique => true, name: "CampaignO"
      t.index [:option_id, :campaign_id], :unique => true, name: "OptionC"
    end
  end
end

Now I'm not sure if the "name" mentioned in the migration file has any play in the way I construct the view or anything, but this is just an example. In my real scenario, I am actually using much longer names than Campaign and Option, so the name field is absolutely required in the migration file.

With all of this being said, I want my Campaign form to show the option associated with it. If it's changed, I want the controller to change its association in the join table. Is this possible?

Here's my view for Campaign.

#app/views/campaigns/index.html.erb
<%= form_with(model: @campaign, local: true) do |form| %>
<%= form.collection_select :option_ids, Option.order("name ASC").all, "id", :name, prompt: "Select an option", class: "form-control" %>
<% end %>

Now when I submit this form, it looks like this in rails:

=> <ActionController::Parameters {"utf8"=>"✓", "authenticity_token"=>"3Dxl5mvuS82BzjM20CYgNgIMay4mNtOv3FY3E79onO/w7yN5wOfwsf20SzEh3VoRN+sLgAo22dhfxuI2zR6O+Q==", "campaign"=><ActionController::Parameters {"name"=>"HEllo World", "option_ids"=>"3"} permitted: false>, "controller"=>"campaign", "action"=>"create"} permitted: false>

The problem is that there is no association being created between Option and Campaign.

Trying to figure out what I'm missing here. I know it's something obvious but I can't figure it out. The only thing I can think of is creating another action in the controller and using that to create an association.

** Edit **

Here is the controller, which, again, is just default:

  def create
    @campaign = Campaign.new(campaign_params)

    respond_to do |format|
      if @campaign.save
        format.html { redirect_back fallback_location: campaigns_path, notice: 'Campaign was successfully created.' }
        format.json { render :show, status: :created, location: @campaign }
      else
        format.html { render :new }
        format.json { render json: @campaign.errors, status: :unprocessable_entity }
      end
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_campaign
      @campaign = Campaign.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def campaign_params
      params.require(:campaign).permit(:name, option_ids: [])
    end
LewlSauce
  • 5,326
  • 8
  • 44
  • 91
  • Can you post the controller code? – Pavan Sep 06 '18 at 14:52
  • I haven't done anything custom in the controller just yet, @Pavan. I guess I was trying to figure out if Rails had an easy way to modify the form so that the controller would "know" that it needs to modify its associations. – LewlSauce Sep 06 '18 at 15:22
  • What the problem currently? Is the option that is being selected isn't saved in the DB when you submit the form? – Pavan Sep 06 '18 at 15:29
  • Exactly. Although it shows in the parameters, it's not saving as an association to the Campaign. – LewlSauce Sep 06 '18 at 15:29
  • Then your controller code is required. Please post it in the question. – Pavan Sep 06 '18 at 15:35
  • You need to whitelist `option_ids` in your strong parameters. `params.require(:campaign).permit(:foo, :bar, option_ids: [])` – max Sep 06 '18 at 15:35
  • @max did this, but no go. I'm going to update the question with my controller, which is essentially just a default controller. – LewlSauce Sep 06 '18 at 15:38
  • I don't think permitting `option_ids` as an **array** is necessary here unless you are actually passing multiple values to it. Try changing it to `params.require(:campaign).permit(:name, :option_ids)` – Pavan Sep 06 '18 at 15:42
  • Changing `option_ids: []` to `:option_ids` did the trick, @Pavan. Thank you guys a lot for the help! – LewlSauce Sep 06 '18 at 15:45

1 Answers1

0

As I said whitelisting option_ids as an array is not necessary here unless you are actually passing multiple values to it. You should change campaign_params to below

def campaign_params
  params.require(:campaign).permit(:name, :option_ids)
end

Also, I recommend changing option_ids to just option_id as you are just selecting a single option at a time. So the campaign_params will be

def campaign_params
  params.require(:campaign).permit(:name, :option_id)
end
Pavan
  • 33,316
  • 7
  • 50
  • 76