我有一个事件模型has many :bands, through: :event_bands
。CCD_ 2模型为CCD_。
在事件创建时,用户从如下多选中选择波段:
<p>
<%= f.label t(:band_playing) %>
<%= f.collection_select :band_ids, Band.order(:name), :id, :name, {}, {multiple: true} %>
</p>
但是,如果用户找不到合适的band,我希望他们能够在我的事件模型中使用accepts_nested_attributes_for :bands
在具有关联的同一表单上创建band。
所以,我把它添加到我的表单中,我用一些jQuery:隐藏/显示它
<div id="new_bands">
<p>
<%= f.fields_for :bands do |ff| %>
<%= ff.label "New Band Name" %>
<%= ff.text_field :name, class: "form-control half" %>
<% end %>
</p>
</div>
然而,为了使fields_for
正常工作,我需要像这样建立关联我的事件控制器:
def new
@event = current_user.events.build
1.times {@event.bands.build}
end
如果用户需要在表单中添加新的标注栏,这将非常有效,但是,如果用户不添加新标注栏,则会破坏表单提交。它断开是因为控制器1.times {@event.bands.build}
中的行构建了一个新的Band,但提交的表单没有通过Band验证的名称。
为了实现这一点,我需要一种在切换表单的fields_for
部分时调用1.times {@event.bands.build}
的方法,但我认为这是不可能的。
我尝试在事件模型accepts_nested_attributes_for :bands, reject_if: proc { |attributes| attributes['name'].blank? }
中使用reject_if
,但它不起作用。我的搜索让我认为这是失败的,因为reject_if
直到模型保存后才运行,但Band.name上的验证是在模型保存前运行的?
现在,我把这个放在我的控制器中,所有的测试都通过了:
def create
@event = current_user.events.build(event_params)
@event.bands.each do |band|
if band.name.blank?
band.destroy
end
end
if @event.save
blablabla
但这似乎违背了整个瘦控制器、胖模型的方法。
一个简单的解决方法:
对强参数使用两种不同的定义。一个包括一个不包括波段参数。
def event_params
params.require(:event).permit(:name, :field1, ..., :fieldn)
end
def event_params_with_band
params.require(:event).permit(:name, :field1, ..., :fieldn, band_attributes: [:name, ..])
end
在@event = current_user.events.build(event_params)
之前应用一些逻辑来合并或不合并频带参数。例如:
def create
if params[:event][:new_band]=="true" # let's assume this comes from a 'tick' in your form
myparams=event_params_with_band
else
myparams=event_params
end
@event = current_user.events.build(myparams)
if @event.save
# your responses etc
end
end
我想你仍然需要为event_bands建立关系,但我想你知道如何做到这一点。