How to dynamically update form elements in Rails using Ajax

Here’s the situation: You have two SELECT elements on a form. When the user chooses an item out of the first SELECT element, the contents of the second SELECT element need to change and show different values based on the initial selection.

This used to take quite a bit of JavaScript wizardry. But it’s pretty straightforward in Rails using a bit of Ajax. Here’s how:

First, make sure you’re including the Ajax libraries (Prototype, script.aculo.us, etc.) in your layout like so:

<html>
<head>
       <%= javascript_include_tag :defaults %>
</head>
<body>
...

Next, create a form with your initial SELECT, and empty second SELECT, and set up field observer. Here’s a really simple example:

<form>

<select id="states" name="states">
    <option value="0"></option>
    <option value="1">Colorado</option>
    <option value="2">Illinois</option>
    <option value="3">Wyoming</option>
</select>

<%= observe_field "states", :update => "cities",
:with => "city_id", :url => { :controller => "test",
:action => "get_cities" } %>

<br />

<select id="cities" name="cities">
    <option></option>
</select>

</form>

Finally, you’ll need to set up a method to handle the call from the observer and a corresponding view page to generate the HTML content that will replace everything betwen the SELECT tags in the second SELECT. Here’s an example "get_cities" controller:

def get_cities
    @cities = City.find_by_state(:all)
end

And an example "get_cities.rhtml" file:

<% for city in @cities %>
    <option value="<%= city.id %>"><%= city.name %></option>
<% end %>

And there you go. This much should dynamically update the "cities" SELECT element with new content every time the "onchange" event is fired on the initial "states" SELECT form element.


Posted: April 12th, 2007 by Neal Enssle
Tags: , , , ,
25 Comments »

25 Comments on “How to dynamically update form elements in Rails using Ajax”

  1. 1 Scott said at 11:54 pm on May 27th, 2007:

    Good stuff
    tnx

  2. 2 Johan Bryntesson said at 12:06 am on June 22nd, 2007:

    Hi!
    I’m a newbie to RoR, so there may be a very simple solution to my problem. But every tutorial I read demonstrates how to update 1) one form field, or 2) several elements via its innerHTML property. But if I want to update several form elements?
    In my case, I have a listbox (say, with users) and a form with user details. When the listbox is clicked I want the form to be filled in, with no page reloading. But I cannot seem to find how to do that in the Rails paradigm (have done it myself with ASP/Javascript, but those days should be over…)

    Regards,
    Johan

  3. 3 neal said at 4:19 am on June 22nd, 2007:

    Johan:

    Great question! Honestly, I haven’t had much of a chance to check this out yet. Have you tried having multiple observers? Perhaps an observer for each field you’d like to update on change?

    I’ll try and check that out here soon and let you know if I can get it working.

    Best,

    Neal

  4. 4 Johan Bryntesson said at 12:36 pm on June 22nd, 2007:

    Neal,
    Actually, an observer for each field (some 15) would probably clog things, since (I believe) each of them would cause a server roundtrip. What I’d like to see (and strongly suspect exist somewhere) is something that could respond to the observed change on a control with something like a hash of arbitrary size looking something like:
    {:name_of_form_control => ‘its new contents’, …}
    causing each control to receive the new value. All in one operation.
    Can’t really believe I am the first with this particular need…

    Thanks for your interest!
    /Johan

  5. 5 Johan Bryntesson said at 4:10 am on June 25th, 2007:

    Neal,
    Found a possible way. I can make a view called edit.rjs, containing:

    page.replace_html ‘divWhereMyFormIs’, :partial => ‘form’, :o bject => @user

    If I surf directly to “localhost:3000/edit/3″ I can see that the constructed HTML is indeed sound. However, due to some strange default behavior, my app won’t do the actual replacing, unless I comment out the line

    @user = User.find(params[:id])

    which enables the above replace_html to fill the partial above with meaningful data…
    It seems I am so close to the solution, but the well-meaning Rails is stopping me…

    Best,
    Johan

  6. 6 Johan Bryntesson said at 9:00 am on June 25th, 2007:

    At last! Excuse my incessant ranting, but I hopa this will be of use to people:

    In my form I have a SELECT named user[list], and the line
    {:action => ‘updateform’}, :with => ‘id’ %>

    The trick is the :with => ‘id’, to tell the subsequent what to map the select’s value to.

    In the controller, the “updateform” action simply reads:
    @user = User.find(params[:id])

    And finally, the “updateform.rjs” consists of:
    page.replace_html ‘formpartial’, :partial => ‘form’, :o bject => @user

    Which automagically repaints the form (which itself is a partial) with the values for the @user object.

    This may be old hat for a lot of people, but I have been cruising the net for three days looking for this solution. So again: hope this helps someone!

    Best,
    Johan

  7. 7 Johan Bryntesson said at 9:02 am on June 25th, 2007:

    Sorry… the line in post #5 starting with {:action =>
    should have read:

    {:action => ‘updateform’}, :with => ‘id’ %>

    Neal, please feel free to edit this to distill out the useful parts. If any!

  8. 8 Johan Bryntesson said at 9:04 am on June 25th, 2007:

    Grrr!! It’s the form that’s editing whar I type!!!
    Again Neal, please edit…

    The line in the form, embedded in less-than + percent

    observe_field ‘user[list]‘, :url => {:action => ‘updateform’}, :with => ‘id’

  9. 9 matt schick said at 3:39 pm on June 28th, 2007:

    Thanks for the walk through, saved me loads of time. And to add to it: one way to make this concept even shorter (albeit a bit more terse) is to use the options_from_collection_for_select in the “get_cities.rhtml” file. For the example above just replace the 3 lines with this one line:

    thanks again!

  10. 10 matt schick said at 3:43 pm on June 28th, 2007:

    ack, I can’t seem to get the damn line of code to post (probably should have read all the comments before posting). Here is the line of code minus the open close brackets, percent signs, and leading equal sign:

    options_from_collection_for_select @states, ‘id’, ‘name’

  11. 11 Chris Ortman said at 12:10 pm on July 11th, 2007:

    Does this work in IE?

  12. 12 Matt Caza said at 5:59 pm on July 12th, 2007:

    Hi. I posted a question on this to http://railsforum.com/viewtopic.php?pid=31045#p31045
    if you have any thoughts, that would be great…

  13. 13 Jake said at 7:03 am on August 30th, 2007:

    I’ve heard that IE doesn’t like to replace the contents of a , so the thing to do here is to wrap the in a span/div/whatever and replace the whole .

  14. 14 Jake said at 7:06 am on August 30th, 2007:

    LOL! Do-over:

    I’ve heard that IE doesn’t like to replace the <option> contents of a <select>, so the thing to do here is to wrap the <select> in a span/div/whatever and replace the whole <select> each time.

  15. 15 Michael said at 7:27 pm on September 17th, 2007:

    This isn’t working for me…

    When I select an option from the first SELECT field, the second SELECT field just grays out. Any ideas?

    Thanks.

  16. 16 Christian Frugard said at 12:33 pm on October 2nd, 2007:

    I am using an :o bserve_field on a series of select elements. It is pretty successful. I think I have found a bug though. When I change the option in a select, the :with parameter is sent, the next element is updated, and everything is good.

    Processing IpController#select_technology (for 0.0.0.0 at
    2007-09-2711:31:39) [POST]Session ID:
    c2e7072af4526b7b7a617e526e3d161bParameters:
    {“ip_info_vw_ring_technology_key”=>”1″,”action”=>”select_technology”,
    “controller”=>”ip”}

    If I change the option in the first select again, quickly (2 seconds later), the action sent to the controller does not contain the :with parameter.
    Processing IpController#select_technology (for 0.0.0.0 at
    2007-09-2711:31:41) [POST]Session ID:
    c2e7072af4526b7b7a617e526e3d161bParameters:
    {“action”=>”select_technology”, “controller”=>”ip”}

    If I wait a little longer between changing options (6-7 seconds),
    it behaves correctly. Is this a bug anyone else has seen?

    Thanks for your help,

    Christian

  17. 17 Sara said at 4:04 pm on April 24th, 2008:

    I can’t get this to work either. I am using Rails 2.0 — does that matter?

    I am using Firefox 2.0. When I click an option in the “states” list, nothing happens. For some reason the “cities” dropdown will not populate.

  18. 18 Sara said at 8:41 pm on April 24th, 2008:

    Hey, I got it to work!! I had to change the following 2 things:

    In the controller, changed the find method to say:

    @cities = City.find_all_by_states_id(params['states_id'])

    and in the view, changed the observe_field statement to say:

    :with => “states_id”,

    It works! Awesome!

  19. 19 Adam Teale said at 1:00 am on September 2nd, 2008:

    Thanks a lot Neal! Your tutorial has giving a far better understanding of observe_field and menus. I almost have it working (no errors in the terminal), but my 2nd menu refreshes with an empty list – no worries, i hopefully can work it out
    Cheers!
    Adam,
    Bangkok

  20. 20 Diego said at 5:41 am on February 6th, 2009:

    You can use the “options_for_select(collection_here)” helper for create the option tags inside desired select tag without iterators or partial layouts. Use it directly like “page.replace_html ’select_id’, options_for_select(collection_here)”.

  21. 21 Jack Zelig said at 11:16 am on April 26th, 2009:

    Hi,
    Thanks for the tutorial. I used this to great effect on the form I am designing for work.
    However, one major issue is that the dynamically created menus don’t survive a page refresh. That is, if you press submit, the form flags an error and thus re-renders itself (highlighting the errors) then the dynamically created menu vanishes (only the initial promt is displayed).
    This was kind of annoying so I wrote a simple javascript routine to recreate and repopulate the field based on what was sent in object(:params).
    I’m sure I can’t be the only one having this problem so if anyone needs any help with this then send me a mail and I’ll be happy to explain how I solved it.
    Thanks again for the tutorial.
    Keep up the good work.

  22. 22 Jack Zelig said at 11:17 am on April 26th, 2009:

    Mail is “jack dot zelig at gmail dot com”
    by the way

  23. 23 bonyiii said at 3:14 am on April 30th, 2009:

    Hi i use “remote_function” within the select element, so i don’t need observer. Just thought to share it:

    remote_function(
    { :controller => “test”,:action => “get_cities” },
    :update=>:cities,
    :with => “city_id”
    )
    } %>

  24. 24 FSS said at 8:47 am on January 12th, 2010:

    Thank you very much!! I´m an experienced programmer but newbie in ruby and rails, ajax and all that jazz and in a rush. All examples found even in books are too simple to be applied. Others in forums get into innecesary trouble. This one is closer to my needs (the two selects come from 2 different tables, and inside a form_for) and updating directly over the named select allowed me to get the job done with really minor changes (the find clause didn´t work for me in that format). Good job Neal.

  25. 25 Amber Puckett said at 7:47 pm on July 26th, 2011:

    Good feed! an address to this blog page was provided by Christian Dillstrom, you are doing a very good job as mobile and social media marketing ace is pointing towards you.


Leave a Reply

http://bestresearchpaper.com/