我当前的工作环境是Rails 2.3.8 (导致公司未迁移到Rails 3的各种原因)。我正在尝试通过AJAX调用更新多模型表单的元素- 这个想法是根据用户选择或填写其他字段的方式替换某些下拉菜单。
我以前已经设法通过使用基于非表单的部分来实现此目的- 我现在的问题是,当部分基于form_for和fields_for时,会重现select下拉列表的AJAX更新。
对不起,下面的文字-我已经尝试过尽可能地减少它(代码本身确实可以在我的测试站点上工作)。
如何在爆发控制器中生成表单构建器元素,然后将其传递给category部分以代替identity_form?
任何指针都很棒:D
楷模
class Outbreak < ActiveRecord::Base has_many :incidents, :dependent => :destroy has_many :locations, :through => :incidents accepts_nested_attributes_for :locations, :allow_destroy => true, :reject_if => :all_blank accepts_nested_attributes_for :incidents, :allow_destroy => true, :reject_if => :all_blank end class Incident < ActiveRecord::Base belongs_to :outbreak belongs_to :location belongs_to :category belongs_to :subcategory belongs_to :subtype end class Location < ActiveRecord::Base has_many :incidents, :dependent => :destroy has_many :outbreaks, :thorugh => incidents end
观看次数
_形成
<% form_for(@outbreak, :html => {:multipart => true}) do |form| %> <%= render :partial => 'outbreak_type_select', :locals => {:outbreak_types => @outbreak_types, :f => form } %> <% form.fields_for :incidents do |incident_form| %> <%= render :partial => 'category_select', :locals => {:categories => @categories, :incident_form => incident_form} %> <%= render :partial => 'subcategory_select', :locals => { :subcategories => @subcategories, :incident_form => incident_form } %> <% end %> <% end %>
_outbreak_type_select
<% with_str = "'outbreak_type=' + value " %> <% if @outbreak.id %> <% with_str << "+ '&id=' + #{outbreak.id}" %> <% end %> <%= f.collection_select(:outbreak_type, @outbreak_types, :property_value, :property_value, {}, {:onchange => "#{remote_function(:url => { :action => "update_select_menus"}, :with => with_str)}"} ) %>
_category_select
调用update_select_menus之后如何生成事故表单
<%= incident_form.collection_select( :category_id, @categories, :id, :name, {:prompt => "Select a category"}, {:onchange => "#{remote_function(:url => { :action => "update_subcategory"}, :with => "'category_id='+value")}"}) %>
RJS
begin page.replace_html 'outbreak_transmission_div', :partial => 'outbreaks/transmission_mode_select', :locals => {:transmission_modes => @transmission_modes } rescue page.insert_html :bottom, 'ajax_error', '<p>Error :: transmission modes update select</p>' page.show 'ajax_error' end begin page.replace_html 'incident_category_select', :partial => 'outbreaks/category_select', :locals => { :categories => @categories } rescue page.insert_html :bottom, 'ajax_error', '<p>Error :: incident category update select</p>' page.show 'ajax_error' end
控制器
暴发
def new @outbreak = Outbreak.new @outbreak.incidents.build @outbreak.locations.build #just the contents for the dropdowns @categories = Category.find(:all, :conditions => {:outbreak_type => "FOODBORNE"}, :order => "outbreak_type ASC") @subcategories = Subcategory.find(:all, :order => "category_id ASC") end def update_select_menus @outbreak_type = params[:outbreak_type].strip if params[:id] @outbreak = Outbreak.find(params[:id]) else @outbreak = Outbreak.new @outbreak.incidents.build @outbreak.locations.build end if @outbreak_type == "FOODBORNE" ob_type_query = "OUTBREAKS:TRANSMISSION_MODE:" << @outbreak_type @transmission_modes = Property.find(:all, :conditions => {:field => ob_type_query}) ob_type_query = "INVESTIGATIONS:CATEGORY:" << @outbreak_type @sample_types = Property.find(:all, :conditions => {:field => ob_type_query}) @categories = Category.find(:all, :conditions => { :outbreak_type => "FOODBORNE"}) @subcategories = Subcategory.find(:all, :conditions => { :category_id => @categories.first.id}) @subtypes = Subtype.find(:all, :conditions => { :subcategory_id => @subcategories.first.id}) elsif @outbreak_type == "NON-FOODBORNE" ob_type_query = "OUTBREAKS:TRANSMISSION_MODE:" << @outbreak_type @transmission_modes = Property.find(:all, :conditions => {:field => ob_type_query}) ob_type_query = "INVESTIGATIONS:CATEGORY:" << @outbreak_type @sample_types = Property.find(:all, :conditions => {:field => ob_type_query}) @categories = Category.find(:all, :conditions => { :outbreak_type => "NON-FOODBORNE"}) @subcategories = Subcategory.find(:all, :conditions => { :category_id => @categories.first.id}) @subtypes = Subtype.find(:all, :conditions => { :subcategory_id => @subcategories.first.id}) end respond_to do |format| format.html format.js end end
根据http://www.treibstofff.de/2009/07/12/ruby-on-rails-23-nested-attributes- with-ajax-support/找到了解决方法
这可能应该放在爆发助手中(在爆发控制器atm中)
def update_select_menus @outbreak_type = params[:outbreak_type].strip #next_child_index will only be used if @next_child_index ? params[:next_child_index] : 0 if params[:id] @outbreak = Outbreak.find(params[:id]) else @outbreak = Outbreak.new @outbreak.risks.build @outbreak.incidents.build @outbreak.locations.build end if @outbreak_type == "FOODBORNE" ob_type_query = "OUTBREAKS:TRANSMISSION_MODE:" << @outbreak_type @transmission_modes = Property.find(:all, :conditions => {:field => ob_type_query}) ob_type_query = "INVESTIGATIONS:CATEGORY:" << @outbreak_type @sample_types = Property.find(:all, :conditions => {:field => ob_type_query}) @categories = Category.find(:all, :conditions => { :outbreak_type => "FOODBORNE"}) @subcategories = Subcategory.find(:all, :conditions => { :category_id => @categories.first.id}) @subtypes = Subtype.find(:all, :conditions => { :subcategory_id => @subcategories.first.id}) elsif @outbreak_type == "NON-FOODBORNE" ob_type_query = "OUTBREAKS:TRANSMISSION_MODE:" << @outbreak_type @transmission_modes = Property.find(:all, :conditions => {:field => ob_type_query}) ob_type_query = "INVESTIGATIONS:CATEGORY:" << @outbreak_type @sample_types = Property.find(:all, :conditions => {:field => ob_type_query}) @categories = Category.find(:all, :conditions => { :outbreak_type => "NON-FOODBORNE"}) @subcategories = Subcategory.find(:all, :conditions => { :category_id => @categories.first.id}) @subtypes = Subtype.find(:all, :conditions => { :subcategory_id => @subcategories.first.id}) end @pathogen_types = Property.find(:all, :conditions => {:field => "PATHOGENS:CATEGORY"}) @outbreak_types = Property.find(:all, :conditions => {:field => "OUTBREAKS:OUTBREAK_TYPE"} ) render :update do |page| page.replace 'outbreak_transmission_div', :partial => 'transmission_mode_select_update' page.replace 'incident_category_select', :partial => 'incident_category_select_update' page.replace 'incident_subcategory_select', :partial => 'incident_subcategory_select_update' page.replace 'incident_subtype_select', :partial => 'incident_subtype_select_update' end end
在“爆发”视图中(尽管由于此部分与“事件”相关,所以它可能应该在该视图中显示)
<% ActionView::Helpers::FormBuilder.new(:outbreak, @outbreak, @template, {}, proc{}).fields_for :incidents,{:child_index => @next_child_index} do |this_form| %> <div id="incident_category_select"> <%= render :partial => 'category_select', :locals => {:incident_form => this_form } %> </div> <% end %>
ActionView :: Helpers :: FormBuilder用于生成所需的fields_for表单-网站文章对此进行了详细介绍。
结果索引由@next_child_index变量设置,该变量可以通过原始AJAX调用传递给控制器(例如@next_child_index = 1,那么结果表单元素名称将是 爆发[incidents_attributes] [1] [category_id] )-我没有在此示例中使用此方法,因为尽管将来我希望该表单在每个爆发中都支持多个位置以进行此初始遍历,但它只接受一个位置-每个爆发均发生一个事件。
_category_select.erb部分(在“爆发”视图atm中)
<% with_str = "'category_id=' + value " %> <% if @outbreak.id %> <% with_str << "+ '&id=' + #{@outbreak.id}" %> <% end %> <%= incident_form.collection_select( :category_id, @categories, :id, :name, {:prompt => "Select a category"}, {:onchange => "#{remote_function(:url => { :action => "update_subcategory"}, :with => with_str)}"}) %>
with_str只是有选择地将爆发ID传递给控制器(如果存在)以找到爆发记录以生成表单,如果不存在则创建一个新的爆发以及关联的嵌套属性(如事件和位置)。
必须采用更整洁的方式进行操作-尤其是FormHelper并通过可选的with字符串传递爆发ID。