在很多時候,下拉選單選項是專門做一張查詢表,顯示裡面的某個欄位,這張查詢表可能會被經常改動。所以,動態的下拉選單是常見的。舉個例子:
在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
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
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)”
add constraint fk_order_pay_types
foreign key (pay_type_id) references pay_types(id)”
def self.down
drop_table :pay_types
end
end
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
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}
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
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
Object.const_get(symbol.to_s.capitalize).find(:all, :order => `name`).collect { |item| [item.name, item.id] }.insert(0, [`Please select…`, nil])
end
通過類名來呼叫方法,得到陣列。