Sorry this page looks weird. It was automatically migrated from my old blog, which had a different layout and different CSS.

Submitting Part Of A Form Via AJAX

Today I was building an order form that allows people to order bespoke suits, tailored just the way they like. The price of an order is calculated from the various cloths the customer chooses and the number of garments, and has various reductions thrown in here and there for good measure.

Calculating the price and displaying it on the order confirmation page was straightforward. But not all of us can say, “If you have to ask how much it costs, you can’t afford it.” Us mere mortals need to know what damage our order for beautiful, made-to-measure Savile Row suits is going to do to our bank account — before we place the order.

That’s easy, I thought. I can just send the relevant parts of the form to the server in the background, calculate the price there, and send the total back.

But how to send just part of a form via AJAX? Rails makes it trivial to submit a whole form remotely but, in this case, the form was quite lengthy and most of the fields were irrelevant to the price calculation. I read engineering and learned that efficiency is a Good Thing. Sending lots of irrelevant data struck me as a Bad Thing. But it wasn’t obvious to me how to send just the form elements I needed.

Here’s the solution I eventually came up with:

First, write a short JavaScript function to capture just the fields you need and return them in a hash. Happily Prototype has a lovely API that makes this easy.

public/javascripts/application.js:

function subset() {
  x_val = $F('dom-id-of-element-x');   
  y_val = $F('dom-id-of-element-y');
  return $H({ x: x_val, y: y_val });
}

where x_val and y_val are the values of the form elements you need.

Then you can use the link_to_remote helper passing — and here’s the crucial step — the :with key.

app/views/your_controller/your_action.rhtml:

<%= link_to_remote 'Calculate price...',
                   :url    => {:action => calculate_price},
                   :update => 'dom-id-to-update',
                   :with   => 'subset()' %>

The :with ensures that the data you want to send is sent rather than the default Form.serialize(this) that sends the whole form.

In your controller your values are available as params[:x] and params[:y].

No inefficiency, no problem. Vorsprung durch technik, as Audi would say.

Andrew Stewart • 30 March 2007 • Rails
You can reach me by email or on Twitter.