我目前的工作环境是Rails 2.3.8 (我的公司没有转移到Rails 3的各种原因)。 我试图通过AJAX调用更新多模型表单的元素 - 这个想法是根据用户选择或填写其他字段的方式替换某些下拉列表。
我以前设法通过使用非基于表单的部分来实现这一点 - 我现在的问题是当部分基于form_for和fields_for时,重现选择下拉列表的AJAX更新。
对于以下文本墙感到抱歉 - 我试图尽可能地将其删除(代码本身在我的测试网站上可以正常工作)。
如何在Outbreak控制器中生成表单构建器元素,然后将其传递给partial类别以取代incident_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
<% 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
<%= 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
答案 0 :(得分:1)
根据http://www.treibstofff.de/2009/07/12/ruby-on-rails-23-nested-attributes-with-ajax-support/
找到解决方法这可能应该在Outbreak helper中(在Outbreak controller 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 partial(在爆发视图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只是可选地传递Outbreak id(如果它存在于控制器中)以查找生成表单的Outbreak记录,如果不存在则构建新的Outbreak以及相关的嵌套属性(如事件和位置)。
必须更简洁的方法 - 尤其是FormHelper并通过带字符串的可选项传递Outbreak id。