Onchange Event Fired from Select Field in Rails Form

In the view there is a regular Rails form and a javascript function that will be triggered when the country select field is changed. The javascript function will make an ajax request to the country_select url with the country code passed as the id variable, e.g., /country_code/us for the United States. I’m also using the Carmen plugin for this example which will provide a list of countries and their respective states/provinces. Not all countries are full supported. More information on Carmen can be found at http://autonomousmachine.com/2009/4/1/carmen-a-rails-plugin-for-geographic-names-and-abbreviations and http://github.com/jim/carmen

<%form_for(@model) do |f| %>
<script type="text/javascript" charset="utf-8">
  function change_state_select(state_code)
 {
    new Ajax.Request('/country_select/'+state_code,
    {
      method: 'get',
      onSuccess: function(transport) {
        $('state_select').replace(transport.responseText);
      }
    });
  }
 </script>
 <%= f.select :country,
   Carmen::COUNTRIES,
   {},
   { :onchange => "change_state_select(this.options[this.selectedIndex].value);" }
%>
<div id='state_select'></div>

***Note the : onchange should really be one word but an emoticon shows up otherwise :onchange :(***
Since not all countries are supported I need to execute some conditional logic in the action country_select. If the country is supported I’ll return a snippet of html containing a select field that my form will use. If the country is not supported I’ll return a text field so that the user can write in their state/province.

class CountrySelectController < ApplicationController
  def country_selecet
      begin
         @states = Carmen::states(params[:id])
      rescue
         @states = nil
      end
      render :partial => "country_select/states"
  end
end

In the final partial that is rendered there is either a select field or a text field

<div id="state_select">
  <% if @states.nil? %>
    <%= text_field_tag :model, :state %>
  <% else %>
    <%= select :model, :state, @states%>
  <% end %>
</div>
  • ruby_noobie

    I am having a real problem with this. Most likely because I am a newb to ruby on rails but I just can’t get this to work at all. Could the javascript code be incorrect? I thought that there should be a ) after the state_code. In the Ajax url, /country_select/ is where? Is this a call to the controller or is country_select a folder and if so where did you put it? I am tearing my hair out here and there is a distinct lack of useful examples for the haml files that I am forced to use. Any help would be appreciated.

  • ruby_noobie

    What did you name your controller? mine is country_select_controller.rb. It is in the controller folder. Also in your example country_select is spelled wrong but I corrected that when I added the code to my project. Do I have to have :model in the tags? I don’t think that I have models for this. And do I need to set this up in route.rb? If so, what would it look like?

  • bseanvt

    i apologize for the text not being very well written. i hope to clarify it here.

    essentially, i’m writing a custom function which makes an ajax request to a url when the state of the select form element is changed.

    the url in the ajax request can be to anything that returns the right data. however, i generated a country_select controller, which lives in app/controllers/country_select_controller.rb, and the necessary view in app/views/country_select/_states.erb, and added this to my routes with
    map.country_select ‘country_select/:id’, :controller => ‘country_select’, :action => ‘country_select’

    troubleshooting this, try visiting the resource /country_select/us, for instance, outside of your ajax request and in your browser. make sure that the route is indeed setup and is working before debugging your ajax calls.

    the partial that the controller returns is then being populated inside the state_select div.

    *also you’re right about the “)” being required. wordpress must have truncated my copy/paste.

    does that help? if you want to create a pastie and share your code that isn’t working, i’d be happy to take a look at it

    http://pastie.org

  • ruby_noobie

    Thank you. Let me run through the items that you listed first, most likely tomorrow morning but possibly tonight and I will let you know. Being new to Ruby, Rails, Ajax, and all that good stuff (sorry….VB and ASP programmer trying to learn something new) I find that many people who put up examples expect the reader to know exactly what they are talking about but I am having to learn these languages and frameworks on the fly without benefit of going through tutorials or books first.

  • bseanvt

    i recommend http://www.amazon.com/Agile-Web-Development-Rails-Programmers/dp/097669400X it’s a great introduction/tutorial book for rails development.

    i know your frustration. i’ve been there as well.

    as a blogger it’s challenging to give the right amount of background knowledge in each post, encouraging beginners as well as those people who have more familiarity w/ the framework.

    however, for me, blogging is more of a resource for my future self. i keep notes on those things which didn’t go so smoothly the first time so i’ll have a reference if i run into the same/similar problem in the future.

  • ruby_noobie

    I understand completely. After searching for days and trying several other ways to use Carmen and get country/state selects to talk, I found your blog. Being that it uses javascript on the page, it was the easiest for me to follow and understand. It is simply the haml that is throwing me off because I have to learn news ways to get the html that I need. I really do appreciate you putting this up because this is the closest I have been to having a working app in well over a week. If I could ask you another question, how exactly would I vist the resource? Mydomain.com/mycontrollername/myaction???? I tried mydomain.com/country_select/country_select/US but I get this page does not exists. I must be doing something wrong and can’t find the documentation on how to do this.

  • ruby_noobie

    Oh….for the routes would I use map.connect or map.country_select just as you have it?

  • bseanvt

    if you’re using haml, you have to be careful bc it’s white space sensitive… the error may just be syntax related.

    for instance, you can’t break up statements across multiple lines w/out the | character. you can w/ erb, but haml will fail.

    Erb this is valid
    < %= link_to "Home",
    root_url %>

    Haml will fail
    = link_to “Home”,
    root_url

    You need something like
    = link_to “Home”, |
    root_url |

    more info on that here
    http://seanbehan.com/ruby-on-rails/link_to_function-rails-ajax-reference/

  • ruby_noobie

    Oh! Okay, let me look through the syntax. But I don’t think that I used multiple lines except with the javascript and I used :javascript filter for that.

    Maybe it is in my partial. Can I make my partial an erb instead of haml?

  • ruby_noobie

    I checked for multiline problems but don’t see any and the country dropdown still does not seem to run through the code and render the state dropdown.

    I am not using models so that could be part of the problem. I will try to change those lines in the _states.erb file and see what happens.

    I even created a folder named country_select and put the partial in there trying to make it work and nothing.

    I can post the code if you would like to see it. I have a feeling that it is something really simple and stupid and I am going to feel like a big idiot when I figure out what it is.

    thanks for all your help.

  • http://seanbehan.com sean

    post the code at pastie.org and also put the error message that it’s giving you.

  • ruby_noobie

    I hope I did that right. I have not used pastie.org before.

    Hopefully, I put everything that you need to see in there.

    thanks,

  • ruby_noobie

    oops. it did not keep the link

    http://pastie.org/1203248

  • http://seanbehan.com sean

    can you post the error message you are receiving in a pastie as well?

  • ruby_noobie

    I am not receiving an error message at all. When I go to the site and pull up the page. It displays fine. When I select something from the dropdown, it changes but it does not change the state dropdown or render the partial. But there are no error messages in the browser or in the production.log.

  • http://seanbehan.com sean

    what does it change to?

  • http://seanbehan.com sean

    what does the output of ./script/server show when the javascript is executed?

  • ruby_noobie

    I tried using firebug but I can’t seem to get it to work with this page. Breakpoints seem to be ignored.

  • ruby_noobie

    The country dropdown shows the selected country but the state dropdown stays the same.

    Can you give me a detailed explanation of what to run and how to run it to see what the output of /script/server is?

  • ruby_noobie

    I can give you the site address if that would help.

  • http://seanbehan.com sean

    are you running this on development w/
    ./script/server ?

    if not, you should be able to get the results from the last request in your log file


    i think that your javascript is failing. instead of updating the states div, try just alerting the response from the ajax request. make sure that the resource is working by visiting if

    domain.com/country_select/US

  • ruby_noobie

    when i go to domain.com/country_select/US I get a meesage that something went wrong and we have been notified. But I don’t get a message.

    Actually, because of the situation that I am in, this testing is being done of the production server.

    In the production.log going to the page only shows a successful load of the page. Selecting a country from the country select does not show up in the log at all.

    Going to domain.com/country_select/US shows the following

    Processing CountrySelectController#country_select (for 70.126.196.3 at 2010-10-06 11:20:51) [GET]
    Parameters: {“action”=>”country_select”, “id”=>”US”, “controller”=>”country_select”}

    ActionView::TemplateError (Carmen::NonexistentCountry) on line #5 of app/views/country_select/_states.erb:
    2:
    3:
    4:
    5:
    6:
    7:

    app/views/country_select/_states.erb:5

  • ruby_noobie

    Dang…..the code got striped. Going over to pastie.org to add that to the bottom

  • ruby_noobie

    Okay….going to domain.com/country_select/US or any other country (I tried AU because I know there are states for US and AU) says that it is a non existant country for Carmen::states. That can’t be possible. Let me go look at the permissions for that plugin….maybe the user does not have permissions to read from the file. I don’t know…grasping at straws here.

  • http://seanbehan.com sean

    instead of params[:id]
    manually enter “US” to see if the error is the param not being set properly

  • ruby_noobie

    In the controller? I changed it but it still does not render the new state select when I select a new country.

  • http://seanbehan.com sean

    i think this is your error

    should just be
    options_for_select(@states)

  • ruby_noobie

    Okay…changing the above allows the domain.com/country_select/AU or US to work.

    But the state select still does not render when you change a country on the page.

  • ruby_noobie

    I had a select already in the show.html.haml so I removed that just in case that was the problem but that didn’t help either.

    I don’t see the page refreshing when a country is selected so it really does not seem that the onchange event is being called at all. But of course, to me the code looks fine.

  • ruby_noobie

    Okay, I changed the onchange event to alert and show the select value and that is working.

    Where else could it be? the controller is working because that has been tested. I guess the partial is rendering correctly because running the domain.com/country_select/US returns the dropdown.

    That only leaves the javascript or it is not seeing my so it can’t replace it. maybe?

  • http://seanbehan.com sean

    so it’s just not updating the page… try changing the id from “state_select” to “state_select_test” of the element that you want updated… maybe it isn’t unique on the page…

    the other thing you can try is to use

    $(‘state_select’).innerHTML = transport.responseText

    in the onSuccess js callback

  • ruby_noobie

    Thanks. Yeah. I did an alert in the onchange and that works. But if I put an alert in the javascript function, it does not pop up the alert.

    I have to assume that for some reason the onchange event is not calling the javascript correctly.

    But I can’t see why not.

    It just looks like the javascript is not running at all.

    I will try what you say and get back to you.

    Thanks,

  • bseanvt

    change the js function from

    function change_state_select(state_code)
    {
    new Ajax.Request(‘/country_select/’ + state_code
    {
    method: ‘get’,
    onSuccess: function(transport) {
    $(‘state_select’).replace(transport.responseText);
    }
    });
    }

    to
    function change_state_select(state_code)
    {
    //alert(state_code);
    alert(“Hello”);
    }

    then toggle those to see if it’s getting called… are you using firefox?

    try installing web developer toolbar and it will tell you if there are any js errors on the page

  • ruby_noobie

    this is strange. alert(“state_code”) shows me the country code selected but alert(“Hello”) will not popup anything.

    Why would that be?

    I am using iceweasel for debian lenny.

  • ruby_noobie

    the web dev toolbar when I select to see errors shows that change_state_select is not defined.

  • bseanvt

    try moving the js code into public/javascripts/application.js

    so it’s just straight up js code… rather than embedded w/in haml

    make sure you’re including the js file though

  • ruby_noobie

    Ha! Haml is so strange….I don’t think that I like it at all. Anyway, alert(“Hello”); does not work unless i change it to alert(‘Hello’);

    When I made that change the the function change_state_select runs and selecting a country from the dropdown pops up the alert window with Hello. The alert with the state_code also works. So it looks like the javascript is being called and responding but nothing is being returned.

    I also don’t understand why there would be an error that change_state_select is not defined. How would I define it?

  • ruby_noobie

    I moved the javascript into the application.js file. I then changed the code to alert(state_code) and the alert shows when the country is changed in the select but with the regular code, it does nothing. Could it be the ajax.request that is the problem?

  • bseanvt

    you said earlier that

    alert(transport.responseText)

    was alerting the data from the request. so it’s working, just not updating the page.

    can you post your js w/ pastie?

  • ruby_noobie

    I still get the message that change_state_select is not defined.

    I also added 2 alerts to the javascript one before the ajax call and one after and neither one is processing but it works if I remove all the other code and just have the alert call.

  • ruby_noobie

    Did I do an alert(transprot.responseText? I remember doing a alert(state_code).

    I can post the javascript. I will do it right now. and then try the alert(transport) thing.

  • ruby_noobie

    Okay the js is up at

    http://pastie.org/1203926

  • ruby_noobie

    I tried adding alert(transport.responseText); after the function(transport) but it does not seem to work. Maybe I put it in the wrong place.

  • bseanvt

    there needs to be a “,” after the + state_code … as it’s the first argument to Ajax.Request method…

    try changing your code out w/ this

    function change_state_select(state_code){
    new Ajax.Request(‘/country_select/’ + state_code, {
    method: ‘get’,
    onSuccess: function(transport){
    $(‘state_select’).innerHTML = transport.responseText;
    }
    });
    }

  • ruby_noobie

    You rock dude!!!!!!!!

    It is working! I can’t believe it!

    You are a God among men!

    Thank you so much. I promise to spend many hours learning Ruby, Rails, and all that goes with it once I get this done.

    Thank you.

  • ruby_noobie

    I am going to put your name in there in the comments explaining all the time that you took to help this newb out.

  • ruby_noobie

    Oh sorry…one more thing. Be sure to update the js code in your example. The comma is missing from there too.

  • bseanvt

    no problem. i’m glad i could help! & good luck w/ the project!