【问题标题】:Rails Best Practices for Refactoring Dynamic BehaviorRails 重构动态行为的最佳实践
【发布时间】:2009-06-22 15:22:20
【问题描述】:

我已经开发了可能的国家/地区选择下拉菜单,我想将此行为分解到地址模型中,这样我就不必复制每个动态行为(视图和控制器之间或多或少的拆分)并且每次我希望用户能够输入完整的地址。

基本上,我正在尝试将我的脚趾深入到 DRY 中。但我不确定在哪里嵌入这些行为。我是使用模型还是助手来构建必要的表单?最重要的是:我在哪里以及如何调用动态行为来更新状态列表?我是否需要一个地址控制器,或者这一切都可以在模型中完成吗?

换句话说,我现在在视图中看到的是这样的:

  # _refine.html.erb
      <tr>
        <td>
        <%= label_tag :dest_country, 'Country: ' %></td><td>
          <%= select_tag :dest_country, 
            options_for_select(Carmen::country_names 
                      << 'Select a country', 
                :selected => 'Select a country'), 
            {:include_blank => true,
            :id => 'country_select',                              
            :style => 'width: 180px',
            :onchange => remote_function(
              :url => {:action => 'update_state_select'},
              :with => "'country='+value")} %>
        </td>      
      </tr>
      <tr>
          <div id="state_select_div">
            <td><%= label_tag :dest_state, 'State: &nbsp&nbsp' %></td>
            <td><%= select_tag :dest_state, 
                        options_for_select(Carmen::states('US').collect{
                                |s| [s[0],s[0]]} << ['Select a state'], 
                                :selected => 'Select a state'), 
                                {:style => 'width: 180px'} %></td>
          </div>      
      </tr>

更新方法在控制器中:

# search_controller.rb

def update_state_select
  # puts "Attempting to update states"
  states = []
  q = Carmen::states(Carmen::country_code(params[:country]))
  states = q unless q.nil? 
  render :update do |page|
    page.replace_html("state_select_div",
    :partial => "state_select",
    :locals => {:states => states }
  )
 end
end

最后,我得到了一个包含正确名称或空白文本字段的部分:

# _state_select.html.erb
<% unless states.empty? or states.nil? %>
      <%= label_tag :dest_state, 'Select a state'  %>
 <br/> <%= select_tag :dest_state, 
                     options_for_select(states.collect{|s| [s[0],s[0]]}  
                         << ['Select a state'], 
                       :selected => 'Select a state'), 
                       {:style => 'width: 180px'} %>
 <% else %>
   <%= label_tag :dest_state, 'Please enter state/province' %><br />
   <%= text_field_tag :dest_state %>
<% end %>

现在,我想做的是能够通过模型关联地址(例如,Person has_one :address)并在创建新人的表单中,能够使用类似的东西

 <%= label_tag :name, 'What's your name?' %>
 <%= text_field_tag :name %>
 <%= label_tag :address, 'Where do you live?' %>
 <%= address_fields_tag :address %>

这可以生成适当的下拉列表,动态耦合在一起,其结果可以通过 Person.address.country 和 Person.address.state 访问。

提前致谢!

【问题讨论】:

  • 您能说得更具体一点,或许可以举一些当前界面与所需界面的例子吗?

标签: ruby-on-rails ruby model-view-controller refactoring dry


【解决方案1】:

首先让我确保我正确理解了代码;当用户更改 Country 选择框并向服务器发出 AJAX 请求时,服务器会发回一个脚本,用正确的条目更新 States 框?

如果是这样,我会使用完全不同的策略。使用dest_country 更改时触发的回调。让这个回调做一个 AJAX GET 请求,例如/countries/{COUNTRY}/states.json(或 .xml)。当然,您需要为此设置路由和控制器。使用返回的值填充 dest_state

更新: 至于分解用户界面代码,请尝试使用部分和帮助程序。 http://guides.rubyonrails.org/layouts_and_rendering.html#using-partials

【讨论】:

    【解决方案2】:

    数据属于模型,表示属于模板(和助手)。 也就是说,我将开始将数据与表示分离并删除无用的代码。

    states.collect{|s| [s[0],s[0]]}
    

    AFAIK,不需要执行此操作。您可以将其简化为

    states.collect{|s| s[0]}
    

    states.collect(&:first)
    

    但最重要的是,您可以在 Carmen 类中提供自定义方法来获取正确格式的数据。

    还可以使用 :prompt => "Select a State" 选项简化以下代码,这样您既不需要追加新的数组项,也不需要手动选择名为“Select a State”的项。

    options_for_select(Carmen::states('US').collect{
                                    |s| [s[0],s[0]]} << ['Select a state'], 
                                    :selected => 'Select a state'),
    

    最后,您可能希望将复杂的助手封装在对象中,以便更容易使用 Test::Unit 套件进行测试。查看this screencast

    说到Controller,换个方式

    # puts "Attempting to update states"
    states = []
    q = Carmen::states(Carmen::country_code(params[:country]))
    states = q unless q.nil? 
    

    进入

    # puts "Attempting to update states"
    states = Carmen::states(Carmen::country_code(params[:country])) || []
    

    当您只能调用一种方法时,请确保不要调用两种方法。改变

    <% unless states.empty? or states.nil? %>
    

    进入

    <% unless states.blank? %>
    

    【讨论】:

    • 谢谢!这些都是我将要实施的好建议,但我试图发现如何将动态更新行为从视图/控制器重构到地址模型、帮助程序或模块中。
    • 对,我已经让该演员表中展示的功能正常工作。我的问题是如何一般地复制此功能,即以 DRY 方式。也就是说: * 每当我需要收集地址时,我应该能够将地址与模型关联 * 当我收集/更新该地址时,我应该能够使用辅助方法来生成表单的地址部分(包括调用某些字段的动态更新。)我不想在需要时复制粘贴动态行为;我已经对其进行了微调,并想对其进行重构。
    【解决方案3】:

    我已经使用 Carmen 和 jQuery 写了一篇关于此的博客文章。该代码是可重用的,因为它存在于我的地址视图的一部分中。我通过表单中的 fields_for "address" 标记使用它。您可以在此处阅读整个条目:http://eric.lubow.org/2009/ruby/rails/country-state-select-using-carmen-and-jquery/

    【讨论】:

      猜你喜欢
      • 2013-10-01
      • 1970-01-01
      • 2014-02-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多