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) 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  
通过类名来调用方法,得到数组。

你可能感兴趣的:(职场,Rails,休闲,下拉菜单,form.select)