Best way to client-side load or hide state/province drop down when country is changed?

Maybe I'm just trying too hard, because I have done this plenty of times with plain old javascript before Rails entered my life. But now that I'm trying to work within the Rails system of form_for and such, I am finding that what should be a simple task has become arduous beyond compare. I'm ready to tear out what little hair I have left, and I have yet to find anything online that addresses what I thought would be a common scenario.

So, the scenario: A user create/edit page. When a user selects US or Canada, the drop down loads accordingly. If he selects a different country, the dropdown disappears. When the form is submitted, the user and his address are saved accordingly.

User: has_one :address Address: has_one :state, has_one :country

edit.rhtml:

I got it working by foregoing a form_for selector for this attribute, and using a manually loaded html selector instead. Seems to work as desired, though it feels a bit hackish.

i dont think that's 'hackish'. Basically you're foregoing some automated magic for more control and specificity which in this case you needed.

True true! Just happy to get it working.

If you want a solution that is more Rails-centric...

First, I created some helper methods just for convenience. (I only needed the state_options one but created the others for testing this solution). The xxx_options simply creates an acceptable collection for populating a Rails select. The xxx_option_tags return a string of option tags with the line breaks removed. That's necessary for their inclusion in a js function below:

in application_helper.rb:   ..   def state_options     Address::STATES.collect{|state| [state, state]}   end

  def province_options     Address::PROVINCES.collect{|state| [state, state]}   end

  def state_option_tags     options_for_select(state_options).gsub("\n", "")   end

  def province_option_tags     options_for_select(province_options).gsub("\n", "")   end   ...

I've got an address partial because I will capture addresses for all kinds of different things (people, customers, businesses, etc). My address partial just consists of a normal 'fields_for :address'. Here's the part relevant to the discussion:

    <%= f.select :state, state_options %>     <%= f.text_field :postal_code %>     <%= f.country_select :country, ['United States', 'Canada'], :selected=>'United States' %>     <%= observe_field :address_country,                       :function=> "switch(value){ case 'United States': $('address_state').show();$ ('address_state').update('#{state_option_tags}'); break; case 'Canada': $('address_state').show();$ ('address_state').update('#{province_option_tags}'); break; default: $ ('address_state').hide();}" %>

Okay, so what's going on... I'm making use of the prototype library packaged with Rails and the baked-in "observe_field" helper. This helper observes the country select field and fires on a change (the country select is rendered with US and Canada at the top before an alphabetical listing of countries, and US is selected by default). The :function=>... part is passing a js-function that will become the method responsible for handling the change in the observe field. It will be wrapped in a function block that looks like: function(object, value) {}. That's where the 'value' field comes from. The rest of the string is just a switch-statement that loads the appropriate values into the select box or hides it all together.

The #{state_option_tag} is where the application_helper.rb comes into play. That is evaluated at run time to give the select options you need. The net effect is that the js swaps out the <option> tags in the address_select field.