rails中使用form.select创建动态的下拉菜单

在很多时候,下拉菜单选项是专门做一张查询表,显示里面的某个字段,这张查询表可能会被经常改动。所以,动态的下拉菜单是常见的。举个例子:
 
在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) references 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  
通过类名来调用方法,得到数组。




本文转自 fsjoy1983 51CTO博客,原文链接:http://blog.51cto.com/fsjoy/89010,如需转载请自行联系原作者
上一篇:猜数字游戏


下一篇:信号与系统2021年期末考试命题