假设我们有两个select,子select的内容需要随父select而动态改变,这篇指南将向你演示如何实现AJAX方式的select层叠:
1. 创建三个资源:country,city,person
ruby script/generate scaffold_resource country name:strin
ruby script/generate scaffold_resource city country_id:int name:string
ruby script/generate scaffold_resource person country_id:int city_id:int name:string
2. 执行rake db:migrate
3. 修改app/views/layouts/people.rhtml,包含prototype:
<%= javascript_include_tag 'prototype' %>
4. 修改app/views/people/new.rhtml:
<p>
<b>Country</b><br />
<%= f.select (:country_id, Country.find_all.collect {|c| [ c.name, c.id ] },
{ :include_blank => true },
nchange => remote_function(:update => "cities",
:method => "get",
:with => "'country_id=' + value + '&partial=select'",
:url => { :controller => :cities,
:action => :index}))
%>
</p>
<p>
<b>City</b><br />
<div id="cities">
<%= f.select (:city_id, []) %>
</div>
</p>
这段代码为country select定义了onchange函数,它会在country select的选择项变化时重新请求cities/index,并更新city select。
5. 现在打开http://localhost:3000/people/new,你会看到:
6. 但是别着急,工作还没完成,cities/index并不会自动帮你返回一个select,因此我们需要修改cities_controller的index方法:
def index
if params[:country_id]
@cities = City.find(:all, :conditions => ["country_id = ?", params[:country_id]])
else
@cities = City.find(:all)
end
respond_to do |format|
format.html { render :partial => params[:partial]}
format.xml { render
ml => @districts.to_xml }
end
end
现在,index action会根据:country_id参数返回指定国家的城市,并根据partial参数来决定如何对数据进行渲染:
format.html { render :partial => params[:partial]}
要将结果渲染为一个select,我们需要定义_select.rhtml,它很简单:
<%= select(:person, :city_id, @cities.collect {|c| [c.name, c.id]}) %>
7. 一切搞掂,现在再次打开http://localhost:3000/people/new,选择中国,你会发现,city select已经跟随你的选择而动态更新了:
8. 很简单,但是还有个问题需要说明,由于IE不支持替换select的innerHTML,因此我们在_select.rhtml中返回了整个 select(本来应该只要options就足够了),注意select的第一个参数是:people,也就是说这个select将只能够为 people/new工作,如果你想要让_select.rhtml更通用些,那就需要再增加一个参数(比如owner)来充当select的第一个参 数。(完)
