December 27th, 2006

scott pilgrim

Ruby on Rails :with and Form.serialize

While looking for a way to make an asynchronous request onBlur, I came upon the :with option, which can be used with Form.serialize to pass form data into (among others) the "holy ajax trinity" methods (form_remote_tag, link_to_remote, and remote_function) without a form submit.

I couldn't find any documentation on this powerful trick¹, so I wanted to write some. I'm a terrible doc writer (you get out of practice when you write self-documenting code), so here's an example instead:

We're building a new form to post a job, and within it is a "form" that lets you add tags to the job, one at a time, without submitting the big form. To get around the messiness of nested <form>s, I created an ajax call that makes it look like the server is saving your tags in a database somewhere and displaying the result, but really it was just an excuse to use rjs to append the tags to a list on the client-side. Nothing's really saved until you submit the job form². It looks somewhat like this³:

link_to_remote "save tags",
               :url => { ... },
               :with => "Form.serialize($('form_tags').form)",

Here's the magic of :with. I'm saying: to execute this ajax request you'll need the contents of this form, which I'm sending as POST params*. On the controller side, you now have access to all form values, in params[:form]. For instance, I wanted to get the comma-separated list of tags into an array, which is as easy as:

@tags = params[:form][:tags].split(",")

Hopefully this will be of use to someone else. Using :with and Form.seralize, you can asynchronously pass the contents of the form to server without "submitting" anything.

¹ Try searching for stuff like "rails form :with" or "link_to_remote with" and see the quality of the results. Google Code Search didn't help either.
² Since until you submit the job itself, there's no entity with which to associate the tags.
³ In the following code, form_tags is the DOM id of the tag field...but there's probably a cleaner way to pass the form element into Form.serialize.
* (Ran out of &sup;s) Form.serialize produces a URL-encoded string that is nearly identical to the one that would be sent if you submitted the form (no images, etc. caveat emptor)