Rails has_many 通过带有附加属性的表单 [英] Rails has_many through form with additional attributes

查看:50
本文介绍了Rails has_many 通过带有附加属性的表单的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个表单,允许用户向活动添加/编辑/删除位置.我目前找到的所有示例要么针对 HABTM 表单(不允许编辑 has_many through 配置中存在的其他属性),要么只列出现有的关系.

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.

该列表将显示每个可用位置.通过campaign_locations 模型有关系的位置将被检查,并且它们的campaign_location 特定属性可编辑.未选中的位置应该能够被选中,输入特定于 Campaign_location 的数据,并在提交时创建新关系.

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.

下面是我目前已经实现的代码.我曾尝试使用 collection_check_boxes,它非常接近我所需要的,只是它不允许我编辑 Campaign_location 属性.

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.

我已经能够成功编辑/删除现有的campaign_locations,但我不知道如何将其合并以显示所有可用的位置(如所附图片).

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).

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

<小时>

查看

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

<小时>

表单 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" />

推荐答案

您遇到的问题是空白位置尚未实例化,因此您的视图无法为其构建表单元素.要解决此问题,您需要在控制器的 newedit 操作中构建空白位置.

The problem you're running into is that the blank locations haven't been instantiated, so your view has nothing to build form elements for. To fix this, you need to build the blank locations in your controller's new and edit actions.

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

然后,在您的 editupdate 操作中,您需要从 params 哈希中删除任何留空的位置,当用户提交表单.

Then, in your edit and update actions, you need to remove any locations that have been left blank from the params hash when the user submits the form.

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 设置一个隐藏字段.

Also, I think you'll need a hidden field for the location_id.

这篇关于Rails has_many 通过带有附加属性的表单的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆