在很多時候,下拉選單選項是專門做一張查詢表,顯示裡面的某個欄位,這張查詢表可能會被經常改動。所以,動態的下拉選單是常見的。舉個例子:
 
在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  

通過類名來呼叫方法,得到陣列。