我正在尝试创建一个表单,允许用户向广告系列添加/修改/删除位置。我目前找到的所有示例都是HABTM
表单(不允许编辑has_many through
配置中存在的其他属性)或仅列出现有关系。
下面的图片显示了我想要完成的任务。
该列表将显示每个可用位置。将检查通过campaign_locations模型建立关系的位置,并使其campaign_location特定属性可编辑。应该能够检查未经检查的位置,输入campaign_location特定数据以及在提交时创建新关系。
以下是我目前实施的代码。我尝试使用collection_check_boxes
,这非常接近我需要的内容,但它不允许我编辑campaign_location属性。
我已经能够成功编辑/删除现有的campaign_locations,但我无法弄清楚如何将其合并以显示所有可用位置(例如附图)。
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
class CampaignLocation < ActiveRecord::Base
belongs_to :campaign
belongs_to :location
end
class Location < ActiveRecord::Base
has_many :campaign_locations
has_many :campaigns, through: :campaign_locations
end
= 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
<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" />
答案 0 :(得分:2)
您遇到的问题是空白位置尚未实例化,因此您的视图无法构建表单元素。要解决此问题,您需要在控制器的new
和edit
操作中构建空白位置。
class CampaignController < ApplicationController
def new
empty_locations = Location.where.not(id: @campaign.locations.pluck(:id))
empty_locations.each { |l| @campaign.campaign_locations.build(location: l) }
end
def edit
# do same thing as new
end
end
然后,在您的edit
和update
操作中,当用户提交表单时,您需要删除params
哈希中留空的所有位置。
class CampaignController < ApplicationController
def create
params[:campaign][:campaign_locations].reject! do |cl|
cl[:pickup_time_start].blank? && cl[:pickup_time_end].blank? && cl[:pickup_timezone].blank?
end
end
def update
# do same thing as create
end
end
另外,我认为你需要location_id
的隐藏字段。
答案 1 :(得分:0)
您应该在模型和表单中添加非模型属性复选框,表示是保存还是删除关系。向表单添加一个带有关系id的隐藏字段,最后根据复选框覆盖accepts_nested_attributes_for
以保存或销毁,并调用super。
class CampaignLocation < ActiveRecord::Base
belongs_to :campaign
belongs_to :location
# Returns true if a saved record, used by form
def option_included
new_record? ? false : true
end
end
class Campaign < ActiveRecord::Base
...
accepts_nested_attributes_for :campaign_locations, allow_destroy: true
def campaign_locations_attributes=(attributes)
attributes.values.each do |attribute|
attribute[:_destroy] = true if attribute[:option_included] != '1'
attribute.delete(:option_included)
end
super
end
end
表格:
= form_for @campaign do |campaign_form|
- locations.each do |location|
= campaign_form.fields_for, :campaign_locations, @campaign.campaign_locations.find_or_initialize_by(location_id: location.id) do |cf|
= cf.check_box :option_included
= location.name
= cf.hidden_field :disease_question_option_id
= cf.datetime_field :pickup_time_start
= cf.datetime_field :pickup_time_end
= cf.text_field :pickup_timezone
如果存在已保存的关系, option_included
将返回true,否则为false。