论坛首页 编程语言技术论坛

基于model动态地ComboBox为生成options

浏览 2789 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2007-06-17  
在使用RoR创建form时,很多时候需要基于model之间的关系创建ComboBox。RoR提供了很多方便的helper方法帮助我们完成一个 form的创建。例如,如果想要为article创建一个form,并且要为article选择category时,可以使用如下方法:
<% form_for(:material, :url => url, :html => { :method => method }) do |f| %>
  <p><label>Name</label><br/>
  <%= f.text_field :name %></p>
  <p><label>Category</label><br/>
  <%= f.select :category_id, get_category_select_options %></p>
  <p><label>Content</label><br/>
  <%= f.text_area :content %></p>
  <p><%= submit_tag 'Create' %></p>
<% end %>

而其中的get_category_select_options可以采用如下实现方式(定义在articles_helper中):
def get_category_select_options  
  Category.find(:all, :order => 'name').collect { |item| [item.name, item.id] }.insert(0, ['Please select...', nil])  
end

以上代码通过Category类找到所有的category,然后使用其中的name和id属性(注意顺序)作为options的label和data,最后在加入一个默认的选项“Please select...”。一切都很简单,不是吗?

然而,如果model之间的关联稍微复杂一点儿,就可能会在很多地方使用类似的方法获得ComboBox的选项,那么为每个可能成为ComboBox选项的model都写这么一个方法实在是太麻烦了。如果是在Java世界中,我们可以很快想到使用反射来解决这个问题。也就是说在知道类名的情况下,用 Class.forName得到Class对象,然后在调用其上的find方法之类的。但是我们使用的是Ruby这个号称动态能力超强的语言,有什么更容易的做法吗?当然,代码如下:
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

关键就在这个const_get方法上,它是Module的一个静态方法,所以我们可以通过Object来调用它。我们把类名传递进去,它就可以返回这个类。但是注意这里的写法,我们可以在返回类上直接调用find,而不是像Java那样clazz.getMethod(...).invoke (...),是不是非常的方便呢?我们可以把这个方法定义在application_helper中,这样在所有的view中就都可以使用了,那么上面的 form代码就变成了如下这样:
<% form_for(:material, :url => url, :html => { :method => method }) do |f| %>
  <p><label>Name</label><br/>
  <%= f.text_field :name %></p>
  <p><label>Category</label><br/>
  <%= f.select :category_id, get_select_options_for(:category) %></p>
  <p><label>Content</label><br/>
  <%= f.text_area :content %></p>
  <p><%= submit_tag 'Create' %></p>
<% end %>
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics