Ajaxed Select Boxes in Rails
posted on Jan 19, 2006 / tags: web, rails

The controller for this exercise includes two methods.  One for displaying the main page, and a second for generating the select box.  Let’s start with the page.  We’ll call it order_list:

def order_list


end

That was easy.  Nothing here.  You’ll see why in the view.

Now for the second method to populate the second select box:

def fill_orders_box
  results = Order.find_all_by_customer_id(@params[:customer_id])
  render :partial => ‘options’
end

There’s a few things here that I wanted to comment on.  The first is the find command on the Order model.  ActiveRecord does this very slick translation when you use the findallby_ or findby followed by the column to search on.  When ActiveRecord sees this, it automagically translates it to the equivalent search string.  For instance the above example translates to:


@results = Order.find(:all,
:conditions => [“customer_id = ?”, @params[:customer_id])

Now let’s be honest… that’s pretty cool.

The second thing is the use of the partial.  In this example I’m using a partial to fill in the select box option tags.  This command makes that happen.

Enjoy the View

The view is where I create the form and put in the first select box, the placeholder for the second select box and the AJAX commands to tie them all together.  Here’s the code:

<%= javascript_include_tag “prototype” %>
<select id=“order[customer_id] name=“order[customer_id]”>
  <%= options_from_collection_for_select(
  Customer.find_all_by_customername, “id”, “customername”) %>
</select>


<span id=“order_id_list”>
  <select id=“order_id” name=“order[id]”></select>
</span>


<%= observe_field(“order[customer_id]”,
  :frequency => 0.25,
  :update => “order_id_list”,
  :url => {:action => :get_orders},
  :with => “‘customer_id=’+value”)
%>

So theres a few things to discuss here.  At the top is the javascript_include tag.  This is telling the view to use the prototype javascript file in the rails package to handle the AJAX calls.  Don’t forget this, it’s what you call ‘important’.

The first select box is straight-forward, but it’s filled with another slick rails deal.  Optionsfromcollectionforselect takes the first parameter (note the findallby thing again) to get the recordset to fill it with (hence nothing in the controller for this), the id field is next, and lastly the text for the option statement.  Very slick indeed!

Next we have the placeholder for the second select box.  In this case I used a span, but div will work too.  Then I put an empty select box so that it’s there from the start, you can leave this out if you want it to appear when you click a customer in the first box.  Also worth mentioning here is something Rory said on his site, you can’t change the innerHTML of a select box in IE, so that’s the reason for the span, we’ll change what’s in there with the partial.

Now for the AJAX.  The last part is the listener.  This makes it so the page is always looking for the first select box to change.  It looks at the frequency of once ever .25 seconds.  When it senses a change, it calls the URL with the included with value as a key and updates the orderidlist span.  There’s a bunch of magic that happens in the background, but let’s face it, that’s the best part about rails!

Partial Credit

The last part is the partial.  This file is called _options.rhtml.  Note the underscore…this is what makes a partial a partial.  Because DHH said so.

<select id=“order_id” name=“order[id]”>
  <% for order in @results do -%>
  <option value=”<%=order.id %>” >
    <%= order.order_number %>
  </option>
  <% end -%>
</select>

That’s it.  Here we generate the select box, loop through the records that are sent from the controller and create a slew of option tags.

So there you have it.  As I said before, I’m fairly new to rails and ruby, so if you find flaws in my code (which shouldn’t surprise you) please leave a comment and let me know.  I hope this helps someone!

Comments

Very nice. One slight improvement that i made was to use the 'order' partial in the view. Easier to maintain in future

posted by Rimu on November 14, 2007

Thanks for this. Shouldn't fill_orders_box be get_orders? Also, if you leave out the :frequency on your observer it will use events instead of polling.

posted by Andrew on May 28, 2008

Yep, you're right on both counts... It's been a long time since I wrote this post, I should really update it!

posted by Adam on May 28, 2008

nice 1, I've tried several techniques to implement dynamic select menus and this is the most sane and easiest to implement. Cheers dude.

posted by Katateochi on September 12, 2008

Well written - this helped me plenty! I was trying to figure out how to do this. Thanks.

posted by James on June 26, 2009

This worked for me if i had only 1 select box to update. It does not work when i have to chain the select boxes. eg) When i select countries, states should get populated and when i select states, cities should get populated. The AJAX request when selecting the state is not submitted to the server. This is probably because the response for the states does not replace the innerHTML of the page. : 'Select country' %> : : 0.25, :update => "states_list", :url => {:controller => :moduls, :action => :get_active_states}, :with => "'country_id='+encodeURIComponent(value)") %> 0.25, :update => "chapters_list", :url => {:controller => :moduls, :action => :get_active_chapters}, :with => "'state_id='+encodeURIComponent(value)") %> Any Ideas? Thanks, Pratik

posted by Pratik on July 04, 2009

Leave a Reply