Accept_nested_attributes_for 部分的 AJAX 更新
我目前的工作环境是Rails2.3.8(由于种种原因我的公司还没有迁移到Rails 3)。 我正在尝试通过 AJAX 调用更新多模型表单的元素 - 这个想法是根据用户如何选择或填写其他字段来替换某些下拉列表。
我之前曾设法通过使用基于非表单的部分来实现此工作 - 我现在遇到的问题是当部分基于 form_for 和 fields_for 时重现选择下拉列表的 AJAX 更新。
抱歉,下面的文字墙 - 我已尝试尽可能地减少它(代码本身确实可以在我的测试站点上运行)。
如何在 Outbreak 控制器中生成表单构建器元素,然后将其传递到部分类别以取代 event_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 后如何生成 event_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
My current working environment is Rails 2.3.8 (various reasons why my company hasn't moved to Rails 3).
I'm trying to update elements of a multi-model form via AJAX calls - the idea being to replace certain dropdowns depending on how the user selects or fills in other fields.
I have previously managed to get this working by using non-form based partials - the problem I have now is to reproduce the AJAX updating of the select dropdowns when the partials are based around form_for and fields_for.
Sorry for the following wall of text - i've tried to cut it down as much as possible (the code itself does work on my test site).
How do I generate the form builder elements in the Outbreak controller and then pass this to the category partial to take the place of incident_form?
Any pointers would be great :D
Models
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
Views
_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
After calling update_select_menus how to generate the 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
Controllers
Outbreak
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
找到了基于 http://www.treibstofff.de/2009/07/12/ruby-on-rails-23-nested-attributes-with-ajax-support/
这可能应该放在爆发助手中(在爆发中控制器 atm)
在 Outbreak 视图中(尽管由于该部分与 Incident 相关,因此它可能应该进入该视图)
ActionView::Helpers::FormBuilder 用于生成表单所需的 fields_for - 网站文章详细介绍了这一点细节。
结果索引由 @next_child_index 变量设置,该变量可以通过原始 AJAX 调用传递到控制器(例如 @next_child_index = 1,则结果表单元素名称将为 outbreak [incidents_attributes] [1] [category_id ] ) - 我在本示例中没有使用此选项,因为尽管将来我希望表单在初始运行中支持每个爆发的多个位置,但它只接受单个位置 - 每次爆发的事件。
_category_select.erb 部分(在 Outbreak 视图 atm 中)
with_str 只是选择性地传递 Outbreak id(如果存在)到控制器以查找 Outbreak 记录以生成表单,如果不构建新的 Outbreak 以及关联的嵌套属性(例如事件和事件)地点。
必须有更简洁的方法来做到这一点 - 特别是 FormHelper 并通过可选的 with 字符串传递 Outbreak id。
Found a work around based on http://www.treibstofff.de/2009/07/12/ruby-on-rails-23-nested-attributes-with-ajax-support/
This should probably go in Outbreak helper (in Outbreak controller atm)
In the Outbreak view (although since this partial is related to Incident it should probably go in that view instead)
The ActionView::Helpers::FormBuilder is used to produce the required fields_for form - The website article goes through this in more detail.
The resulting index is set by the @next_child_index variable which can be passed to the controller by the original AJAX call (for example @next_child_index = 1, then the resulting form element name will be outbreak [incidents_attributes] [1] [category_id] ) - I haven't used this in this example because although in future I want the form to support more than one location per Outbreak for this initial run-through it will just accept a single Location - Incident per Outbreak.
_category_select.erb partial (in Outbreak view atm)
The with_str is just to optionally pass the Outbreak id if it exists to the controller to find the Outbreak record to produce the form and if not to build a new Outbreak and associated nested attributes like Incidents and Locations.
The must be neater ways of doing this - especially the FormHelper and passing the Outbreak id via the optional with string.