views:

2015

answers:

3

I am trying to implement something seemingly very simple, and I have been beating my head against it for days at this point.

My desired end result is a Country select drop-down, tied to a State select drop-down, in such a way that when a given country is selected, IF states are known THEN those states are displayed in a select drop down, and if NO states are known for that country, then a text field is displayed instead.

I feel like I am almost there. At this point the interface will actually generate that list of states based on the persons' country, except it is refusing to update the drop-down dynamically.

The portion of my view where country and state location is gathered looks like:

# _person_setup.html.erb
         <td>
        <%= f.label :country, 'Select your country' %>*<br />
        <%= f.select :country, Carmen::country_names, {}, 
          {:style => 'width: 200px', 
          :id => 'country_select',
          :onchange => remote_function(
            :url => {:action => 'update_states'},
            :with => "'country='+value")} %>            
      </td><td>
        <p>
        <div id="states_div">
            <%= render :partial => 'states', 
                :object => Carmen::states(
                            Carmen::country_code(
                              @person.country)),
                :locals => {:form => f} %>
       </div>
        </p>            
      </td>

The partial being referenced in the DIV is as follows:

 # _states.html.erb
<% unless states.nil? or states.empty? %>
    <%= form.label :state, 'Select your state' %>*<br />
    <%= form.select :state, states.collect{|s| [s[0], s[0]]} %>
<% else %>
    <%= form.label :state, 'Please enter state or province' %>*<br />
    <%= form.text_field :state %>
<% end %>

Finally, the controller code which is intended to update the list of states dynamically:

def update_states    
puts "Attempting to update states..."    
q = params[:country]    
states = Carmen::states(Carmen::country_code(q))
puts "Country = #{q}, states = #{states.collect{|s| s[0]}.join(", ")}."
render :update do |page|
    page.replace_html "states_div", 
      :partial => 'states',
      :object => states,
      :locals => {:form => form_for(@person)}
end
puts "OK"
end

Now, this code is being called at the proper time and generating the appropriate lists of states. For example, when the user clicks Australia, "Attempting to update states... Country = Australia, states = Australian Capital Territory, New South Wales, Northern Territory, Queensland, South Australia, Tasmania, Victoria, Western Australia" shows up in the server process. However it doesn't update the page, and won't print the "OK" at the end. In short the line which is failing is undoubtedly

page.replace_html "states_div", 
      :partial => 'states',
      :object => states,
      :locals => {:form => form_for(@person)}

Note that replacing this line with

page.replace_html 'states_div', "<b>is it working</b>"

properly replaces the div, but of course not with anything useful.

Can someone help me understand what is going on here?

A: 

Ryan Bates has a Railscast that shows how to select a category for a product or create a new category by typing the name. It sounds like a similar scenario to what you have, so you might want to check it out: Create Model Through Text Field.

John Topley
Thanks! But not exactly what I'm looking for. I don't need to create new States based on user input; I just need to dynamically populate a selection list. Now, #88 is almost exactly what I'm trying to do, but I couldn't get his method to work with Carmen. Ryan uses State and Country models, it just seemed messy to me (maybe less messy than mixing view code in the controller, but if I can get it working -- i.e., with geolocation and everything -- I plan to break the entire location-gathering mechanism out into a Location model/helper.)
Joe
A: 

It looks like you're assuming that the @person variable is still available from your original action. This could be set up by a filter for the current person but you don't show that in your question.

If you do need to lookup the @person again you'll have to pass the id through in your remote_function I think.

Shadwell
OK, I'm passing the id into the update_states function and looking up the person again. But it's still not updating the form dynamically.Is the problem that I'm trying to create a variable for a form? In other words, is "f = form_form(@object)" valid within the controller, and can you pass this variable to a partial?
Joe
That's supposed to be "f = form_for(@object)", of course.
Joe
Are you actually getting any errors back or in the logs?It is possible that it doesn't like the form_for in the render in update_states. Might be worth trying with : "select :person, :state ... " instead of the form_for stuff.
Shadwell
OK, that was actually it. You can't create form scope like that from the controller -- and you shouldn't be able to either.Once I passed the person into the partial and created a new form scope from there, it all started working.Thanks, Shad.
Joe
A: 

This took me a full day to figure out something that would at least "work". I am also using Carmen and also messing with the select tag in a form_for model form (actually, fields_for nested within forms_for...which adds additional complications).

I would think there is a better solution but this worked for me. The select needs to be referenced by the form but the options don't. Thus, first time through, I use the Carmen state_select method which populates the select tag correctly and all the nested options tags. The second time through, I just replace the options. Take a look:

In the view, I chose to use an observe_field method since I do other things besides update the states (some other localization changes) but this should work for remote_function and others, too:

<%= address_form.country_select :country, {:prompt => "--Select One--"} %>

<%# Don't be confused by the id (user_address_attributes_country) it is just my silly forms_for/fields_for implementation)

<%= observe_field :user_address_attributes_country, :url => { :action => :changecountry }, :with => 'new_country' %>

<%= address_form.state_select :state, @user.address.country, {:prompt => "--Select One--"}, :style => 'width: 90px' %>

Then in my controller, it just looks like this:

def changecountry c = params[:new_country]

respond_to do |format|
  format.html
  format.js {
    render :update do |page|
      page['user_address_attributes_state'].innerHTML = \
        "<option>--Select One--</option>" + state_options_for_select(nil, c)
    end
  }
end

end

Note: state_options_for_select is also from Carmen. I could not get it to work unless I put it inside the respond_to block where I guess the view helpers are available. Also, I hard code user_address_attributes_state which is my nested id generated from the form_for/fields_for address_form.state_select rendering call in the view.

I hope this helps. If anyone can do it better, believe me, I'm all ears. I'll change the design in time...but needed something that just worked today and this was the best I could figure out in a limited time.

Kevin

Kevin