在很多时候,下拉菜单选项是专门做一张查询表,显示里面的某个字段,这张查询表可能会被经常改动。所以,动态的下拉菜单是常见的。举个例子:
在depot的实例中,收集订单信息时,有一项是选择付款方式,原例中是做成了一个死的数组形式,针对这个例子,下面进行改进,将这个死的下拉菜单变成动态形式。
首先对于form.select方法来说,接受参数第一个是对象的字段,第二个是一个数组,数组的形式是[[display1, value1],[display2, value2],[display3, value3]] display对应于下拉菜单的选项显示内容, value是要存入对象字段的值。
理解了这点之后,我们着手改进。
思路是新建一个pay_types的表,里面存储付款类型和id号,在orders中修改pay_type字段为pay_type_id字段,作为外键与pay_types表进行约束。
那么现在pay_types表中的两个字段用来构造上面那个数组[[display1, value1],[display2, value2],[display3, value3]] ,其中付款类型为display, id为value。在最终提交表单的时候将value存到orders表的pay_type_id字段。
实现过程以及代码如下:
首先是数据迁移的代码:
1
class CreateOrders < ActiveRecord::Migration
def self.up
create_table :orders do |t|
t.column :name, :string
t.column :address, :text
t.column :email, :string
t.column :pay_type_id, :integer, :null=>false
end
end
def self.down
drop_table :orders
end
end
2
class CreatePayTypes < ActiveRecord::Migration
def self.up
create_table :pay_types do |t|
t.column :tp, :string, :null=>false #付款类型
end
end
#SQL语句,建立约束
execute "alter table orders
add constraint fk_order_pay_types
foreign key (pay_type_id) reference s pay_types(id)"
def self.down
drop_table :pay_types
end
end
在Order模型类中加 belongs_to :pay_type
在PayType模型类中加has_one :order
收集订单信息
...
<fieldset>
<legend>
Please Enter Your Details
</legend>
<%form_for :order, :url=>{:action=>:save_order} do |form|%>
<p>
<label for="order_name">Name:</label>
<%=form.text_field :name, :size=>40%>
</p>
...
<p>
<label for="order_pay_type">Pay with:</label>
<%=form.select :pay_type_id, @types, :prompt=>"select a payment method"%>
</p>
<%=submit_tag "Place Order"%>
<%end%>
</fieldset>
...
这个@types在checkout这个action中这样定义:
@types=PayType.find_types
在PayType类中定义find_types:
#类方法,生成数组
def self.find_types
PayType.find(:all, :order => 'tp').collect { |type| [type.tp, type.id] }
end
在Order类中加入验证:
validates_presence_of :name, :address, :email
validates_format_of :email, :with=>/^[a-zA-Z0-9_\.]+@[a-zA-Z0-9-]+[\.a-zA-Z]+$/
validates_inclusion_of :pay_type_id,:in => PayType.find_types.map{|disp,value| value}
------------
但是,当所有数据全部填写正确,下拉菜单中选择了付款方式后,一切都没问题,但是如果有一项没有填写正确,包括下来菜单,也就是说无论当order对象的哪个字段成为null值,就会报错。错误信息如下:
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.inject
Extracted source (around line
#25):
22:
23: <p>
24: <label for="order_pay_type">Pay with:</label>
25: <%=form.select :pay_type_id, @types, :prompt=>"select a payment method"%>
26: </p>
27:
28: <%=submit_tag "Place Order", :class=>"submit" %>
期待解决。。。
在AllenYoung 的文章中介绍了一个方法,当model变得多且复杂,要用到的下拉菜单多的情况下可以在applicationhelper.rb中定义下面的方法:
def get_select_options_for(symbol)
Object.const_get(symbol.to_s.capitalize).find(:all, :order => 'name').collect { |item| [item.name, item.id] }.insert(0, ['Please select...', nil])
end
通过类名来调用方法,得到数组。