andrew burke
dot
me

about

contact

blog

rss

It's the Little Things - ajax_collection_select and Rails 1.1

Posted on: 2006-07-02

I've wanted to upgrade my various applications to Rails 1.1 ever since it came out a few months ago. I tried upgrading a while back, but the upgrade broke one of my favorite AJAX helper methods - and since there were deadlines, I went back to 1.0 and decided I'd figure it out later.

Well, it's a long weekend and instead of hanging out on a dock somewhere and drinking beer, I've decided to try upgrading again and see if I could figure out what's going on. Finally figured it out. In the interest of helping anyone out there who may be googling for a similar issue, here are the ugly details.

Everything runs fine in the upgrade except for one really handy helper method I use a lot. It's an extended version of the collection_select helper that automatically triggers an AJAX update when the selection is changed. I noticed that the built-in helpers used watchers, which spreads the functionality around into multiple places, and also causes lots of reloads in Firefox, where scrolling through the menus will trigger a reload. My helper is like this:

  def collection_select_ajax(name, property, collection, key, value, update_div, url_params)
      url = url_for(url_params)
      collection_select(name, property, collection, key, value, E'', {:onchange => "new Ajax.Updater('#{update_div}', E'#{url}', {asynchronous:true, evalScripts:true, parameters:value})"})
  end

Handy. Two lines of code - one if I wanted to roll up the line that builds the url - and my life is much easier. However, this breaks in Rails 1.1 - it brings up a 'Cannot convert Symbol into String' error.

Poking around in the actual Rails form_options_helper code, I discovered that the Rails 1 collection_select builds an InstanceTag and calls to_collection_select_tag, which then calls stringify_keys on the options. The new form_options_helper code tries to clean things up first, by calling .delete(:object) on the passed options before sending them on to to_collection_select_tag.

I had always used '', the empty string, as a placeholder if nothing was being passed to collection_select. Turns out it actually wants {}, an empty hash. The earlier version just converted it to a String anyhow, so it didn't matter, but the new version depends on getting a hash.

Changing the '' to a {} makes it work:

  def collection_select_ajax(name, property, collection, key, value, update_div, url_params)
      url = url_for(url_params)
      collection_select(name, property, collection, key, value, {}, {:onchange => "new Ajax.Updater('#{update_div}', E'#{url}', {asynchronous:true, evalScripts:true, parameters:value})"})
  end

I could write something here about statically-typed languages vs. dynamically-typed languages, but instead I'll just say how great it is to have an open-source framework written in a clean enough language that looking at the source code actually makes sense.

Now, if only getting RMagick to work on OS X 10.4 was this easy!

Previous: Quote of the Day
Next: RMagick - oh the humanity!