Лучшие практики Rails по рефакторингу динамического поведения - PullRequest
2 голосов
/ 22 июня 2009

Я разработал условные выпадающие списки выбора страны и государства, и я хотел бы выделить это поведение в модель адреса, чтобы мне не приходилось копировать динамическое поведение (более или менее разделенное между представлением и контроллером) каждый и каждый раз, когда я хочу, чтобы пользователь мог ввести полный адрес.

По сути, я пытаюсь погрузить пальцы немного глубже в СУХОЙ. Но я не уверен, куда именно встраивать поведение. Использую ли я модель или помощника для создания необходимых форм? Самое главное: где и как я могу вызвать динамическое поведение для обновления списков состояний? Нужен ли мне контроллер адресов или все это можно сделать внутри модели?

Другими словами, то, что я сейчас вижу, выглядит примерно так:

  # _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.

Заранее спасибо!

Ответы [ 3 ]

2 голосов
/ 24 июня 2009

Позвольте мне сначала убедиться, что я правильно понимаю код; когда пользователь изменяет поле выбора страны, и на сервер отправляется запрос AJAX, который затем отправляет обратно скрипт, который обновляет поле состояний с правильными записями?

Если так, я бы использовал совершенно другую стратегию. Используйте обратный вызов, который срабатывает при изменении dest_country. Позвольте этому обратному вызову выполнить запрос AJAX GET, например, /countries/ndomCOUNTRY‹/states.json (или .xml). Конечно, для этого вам нужно настроить маршрут и контроллер. Заполните dest_state возвращаемыми значениями.

UPDATE: Что касается выделения кода пользовательского интерфейса, попробуйте использовать партиалы и помощники. http://guides.rubyonrails.org/layouts_and_rendering.html#using-partials

1 голос
/ 22 июня 2009

Данные принадлежат моделям, а презентация - шаблонам (и помощникам). Тем не менее, я бы начал отделять данные от представления и удалять бесполезный код.

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

AFAIK, это выполнение не требуется. Вы можете упростить его до

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

или

states.collect(&:first)

но самое главное, вы можете предоставить собственный метод в своем классе Кармен для получения данных в правильном формате.

Также следующий код можно упростить с помощью параметра: prompt => «Выбрать состояние», чтобы вам не приходилось ни добавлять новый элемент массива, ни вручную выбирать элемент под названием «Выбор состояния».

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

Наконец, вы можете инкапсулировать сложные помощники в объекте, чтобы было проще тестировать их с помощью Test :: Unit Suite. Проверьте этот скринкаст .

Говоря о контроллере, измените

# 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? %>
0 голосов
/ 10 марта 2010

Я написал сообщение в блоге об этом, используя Carmen и jQuery. Код можно многократно использовать, потому что он живет частично в моих адресных представлениях. Я использую его по тегу fields_for "address" в форме. Вы можете прочитать всю запись здесь: http://eric.lubow.org/2009/ruby/rails/country-state-select-using-carmen-and-jquery/.

...