I am attempting to create a form that allows a user to add/edit/remove locations to a campaign. All the examples I have currently found are either for HABTM
forms (that do not allow the editing of additional attributes that exist in a has_many through
configuration) or only list out the existing relationships.
Below is an image showing what I am trying to accomplish.
The list would show every available location. Locations that have a relationship via the campaign_locations model will be checked and have their campaign_location specific attributes editable. Locations that are non-checked should be able to be checked, campaign_location specific data entered, and a new relationship created upon submission.
Below is the code I currently have implemented. I have tried making use of collection_check_boxes
, which is very close to what I need except it does not allow me to edit the campaign_location attributes.
I have been able to successfully edit/remove existing campaign_locations, but I cannot figure out how to incorporate this to also show all available locations (like the attached image).
Models
campaign.rb
class Campaign < ActiveRecord::Base
has_many :campaign_locations
has_many :campaign_products
has_many :products, through: :campaign_products
has_many :locations, through: :campaign_locations
accepts_nested_attributes_for :campaign_locations, allow_destroy: true
end
campaign_location.rb
class CampaignLocation < ActiveRecord::Base
belongs_to :campaign
belongs_to :location
end
location.rb
class Location < ActiveRecord::Base
has_many :campaign_locations
has_many :campaigns, through: :campaign_locations
end
View
campaign/_form.html.haml
= form_for @campaign do |campaign_form|
# this properly shows existing campaign_locations, and properly allows me
# to edit the campaign_location attributes as well as destroy the relationship
= campaign_form.fields_for :campaign_locations do |cl_f|
= cl_f.check_box :_destroy, {:checked => cl_f.object.persisted?}, false, true
= cl_f.label cl_f.object.location.title
= cl_f.datetime_field :pickup_time_start
= cl_f.datetime_field :pickup_time_end
= cl_f.text_field :pickup_timezone
# this properly lists all available locations as well as checks the ones
# which have a current relationship to the campaign via campaign_locations
= campaign_form.collection_check_boxes :location_ids, Location.all, :id, :title
Portion of Form HTML
<input name="campaign[campaign_locations_attributes][0][_destroy]" type="hidden" value="true" /><input id="campaign_campaign_locations_attributes_0__destroy" name="campaign[campaign_locations_attributes][0][_destroy]" type="checkbox" value="false" />
<label for="campaign_campaign_locations_attributes_0_LOCATION 1">Location 1</label>
<label for="campaign_campaign_locations_attributes_0_pickup_time_start">Pickup time start</label>
<input id="campaign_campaign_locations_attributes_0_pickup_time_start" name="campaign[campaign_locations_attributes][0][pickup_time_start]" type="datetime" />
<label for="campaign_campaign_locations_attributes_0_pickup_time_end">Pickup time end</label>
<input id="campaign_campaign_locations_attributes_0_pickup_time_end" name="campaign[campaign_locations_attributes][0][pickup_time_end]" type="datetime" />
<input id="campaign_campaign_locations_attributes_0_location_id" name="campaign[campaign_locations_attributes][0][location_id]" type="hidden" value="1" />
<input id="campaign_campaign_locations_attributes_0_pickup_timezone" name="campaign[campaign_locations_attributes][0][pickup_timezone]" type="hidden" value="EST" />
<input name="campaign[campaign_locations_attributes][1][_destroy]" type="hidden" value="true" /><input id="campaign_campaign_locations_attributes_1__destroy" name="campaign[campaign_locations_attributes][1][_destroy]" type="checkbox" value="false" />
<label for="campaign_campaign_locations_attributes_1_LOCATION 2">Location 2</label>
<label for="campaign_campaign_locations_attributes_1_pickup_time_start">Pickup time start</label>
<input id="campaign_campaign_locations_attributes_1_pickup_time_start" name="campaign[campaign_locations_attributes][1][pickup_time_start]" type="datetime" />
<label for="campaign_campaign_locations_attributes_1_pickup_time_end">Pickup time end</label>
<input id="campaign_campaign_locations_attributes_1_pickup_time_end" name="campaign[campaign_locations_attributes][1][pickup_time_end]" type="datetime" />
<input id="campaign_campaign_locations_attributes_1_location_id" name="campaign[campaign_locations_attributes][1][location_id]" type="hidden" value="2" />
<input id="campaign_campaign_locations_attributes_1_pickup_timezone" name="campaign[campaign_locations_attributes][1][pickup_timezone]" type="hidden" value="EST" />
<input name="campaign[campaign_locations_attributes][2][_destroy]" type="hidden" value="true" /><input id="campaign_campaign_locations_attributes_2__destroy" name="campaign[campaign_locations_attributes][2][_destroy]" type="checkbox" value="false" />
<label for="campaign_campaign_locations_attributes_2_LOCATION 3">Location 3</label>
<label for="campaign_campaign_locations_attributes_2_pickup_time_start">Pickup time start</label>
<input id="campaign_campaign_locations_attributes_2_pickup_time_start" name="campaign[campaign_locations_attributes][2][pickup_time_start]" type="datetime" />
<label for="campaign_campaign_locations_attributes_2_pickup_time_end">Pickup time end</label>
<input id="campaign_campaign_locations_attributes_2_pickup_time_end" name="campaign[campaign_locations_attributes][2][pickup_time_end]" type="datetime" />
<input id="campaign_campaign_locations_attributes_2_location_id" name="campaign[campaign_locations_attributes][2][location_id]" type="hidden" value="3" />
<input id="campaign_campaign_locations_attributes_2_pickup_timezone" name="campaign[campaign_locations_attributes][2][pickup_timezone]" type="hidden" value="EST" />