Archive for the ‘Ruby’ Category

The REAL Rails AJAX Select Control Tutorial

April 1, 2006 in Javascript,Programming,Ruby,Ruby on Rails | Comments (0)

I’ve been trying for a while to implement functionality in Rails where, when the value of one selector is changed by the user, a related selector is refilled with associated data. I’m rewriting one of my PHP applications (The CIty of Villains Registry) in Rails, so I’ve already implemented similar functionality in PHP. Unfortunately, the amount of documentation out there for this sort of thing is rather sparse, and leaves a couple of wide holes open for developers to fall into. I finally got it to work, but it was tough pulling all of the information together. I figured it would be worth my while to post what I did to help others wrestling with the same thing (and there appear to be quite a few).

Create a Partial Template to Render your Select Control

For my application, there is one select control for choosing a hero archetype. Two other selectors let you choose primary and secondary powersets. We’ll just concentrate on the primary powerset selector for this exercise. When the page loads, whatever value is in the archetype selector will determine what values should be available in the powerset selector, so my template does a quick find based on whatever archetype ID is buried in the hero model and generates a select control with the matching powerset names and ID’s.

<%=select(“hero”, “primary_powerset_id”, ArchetypePrimary.find_all_by_archetype_id(archetype_id).collect {|p| [ p.powerset.name, p.powerset_id ] })%>

For my application, this got saved as app/views/hero/_primary_powerset_selector.rhtml

Modify your Page Template to render your Select Control

Now that we have a partial template, we need to get it to render in the parent page. Keep in mind, though, that with an AJAX call, we’re going to be replacing the content of an object on the page with the rendered output, so we’re going to bury the render inside a span tag. We’ll use that name of that tag later on when we make our AJAX call.

<span id=’primary_powerset_selector’>
<%= render :partial => ‘primary_powerset_selector’, :locals => {:archetype_id => @hero.archetype_id}%>
</span>

In my application, this code goes in the app/view/hero/_form.rhtml file where I want the select control to appear.

Create an Action to Render your Partial Template

What we’ve done so far will make the filtered selector appear on the page, loaded with the filtered set of results. In order to make it dynamic, we have to build a custom action into our controller that knows how to refresh the select control. It will take the new archetype ID as an argument, then render our template to produce a select control. It’s a pretty simple call, it just takes the passed argument and renders the partial:

def fill_primary_powerset_box
render(:partial => ‘primary_powerset_selector’, :locals => {:archetype_id => @params[:archetype_id]})
end

In my application, this method was added to the app/controllers/hero_controller.rb file.

Add the AJAX Call to your Page Template

Everything is in place, so now we can ask Rails to render the AJAX call that will dynamically render the select control into our span. Somewhere after the span, drop something like this into your template:

<%= observe_field(
‘hero_archetype_id’,
:update => :primary_powerset_selector,
:url => {:controller => ‘hero’, :action => :fill_primary_powerset_box},
:with => “‘archetype_id=’ + value”)
%>

So what’s worth noticing is the following: I’m passing in the name of the selector that is being watched (when my hero_archetype_selector changes I want this event to be fired), I’m giving it the name of the span I want to be updated with the new content (primary_power_selector is the name of my span), I’m telling it to user the hero controller and call the fill_primary_powerset_box action, and I’m handing the value of the control that changed as an argument named archetype_id.

If you’ve been trying to figure this stuff out for any time at all you’re going to notice one that is missing: The frequency parameter. Every example I have seen for observe_field passes in a frequency parameter, but you only need to use this if you really need to be polling, like maybe you’re pulling in search result as the user types, or dynamically rendering the text being typed somewhere else on the page. You don’t need it to link two select controls together. Leaving this parameter out makes the event fire when the value in the selector changes. I had started out using a frequency of zero, which would make the event fire when it should, but the Prototype library would throw an error. This is because it was sending my zero as the callback instead of a function reference. Just leave it out and you’ll be fine.

It was a lot of working figuring out how to set this up right for the incredibly small amount of code it actually takes to pull it off. Hopefully this will make it easier for you set up a similar configuration in your own applications.

Caveats

Now that I have it working I do notice that the callback is considerably slower than my homebrew PHP version was. Maybe it’s because I’m on a shared server, and maybe it’s because I’m running in dev mode right now. I have to poke around at it a little bit more.

Helpful resources:

  • Rails Hosting – This site has some good examples of using the Prototype library for AJAX and client side scripting with Ruby.
  • Using Prototype v1.4.0 – This helped me ultimately deduce the problem in my rendered calls since it describes the actual constructors and arguments to the Prototype objects. I could see where Form.Element.EventObserver did not take a frequency argument, and that my frequency value of zero was being passed in place of the callback.
  • Rory Hanson’s – This was where I started, since it came up first in Google. It gives some good examples of how to set up the code. You’ll notice, however, the number of people having difficulty making it work.
  • Adam C. Hegedus’s Blog Entry – This one really helped me get started, picking up where Rory’s blog left off.

Ruby on Rails

January 24, 2005 in General,Ruby | Comments (0)

I need to look at this. Can’t tell if it’s important to me or not. I love Ruby, and I’ve spent considerable time on an application framework that runs over the web and standalone in Windows (although not Ruby based). Even if it’s not a tool that I would have a use for, I would like to see how similar our development concepts were, if at all.


CocoaDev: RubyCocoa

January 9, 2005 in Ruby | Comments (0)

I love Ruby as a scripting language, and used to work extensively with Objective-C (which is now Cocoa in the Apple environment). This project appears to combine the two. Interesting, but I can’t imagine when I’ll find time to invest any hours in it. Something to keep in the back pocket, though.

I like to be a “right tool for the right job” type of developer, and I’m always intrigued by new tools. However, if you stray too far from the beaten path, it can be more difficult to find additional staff to support the projects you develop when you’re ready to move on to something new.


Syntax Hell

January 3, 2004 in Programming,Ruby | Comments (0)

I don’t like being a specialist. I enjoy working with lots of different technologies and different programming languages. That’s the good news. The bad news is that, when you work with all of these languages, you have to keep all of the syntax straight between them. All languages support certain base constructs like loops, but they all implement them with slight syntactic difference. Because of this, I get stuck on the stupidest things sometimes. This afternoon I was writing a quick Ruby script to remove bad, frozen messages from my exim queue. I could not for the life of me remember how to get out of a Ruby loop early. Is it next? loop? continue?

continue is the right answer. I had left my pickaxe book at the office, so I was looking it up online. Lots of resources for how to get into Ruby loops, but the ones that told you how to get out were being elusive. I had even tried continue earlier, but I must have fat-fingered the keyword because I got an error, so I kept looking for something different. It’s those kinds of loops, where you keep searching for the answer even though you actually already found it, that drive me up the wall.

So much for giving anything back today, eh?