Jan 17, 2009

jQuery: unobtrusive javascript form submit with Ajax in Merb.

I was working on a project this week. There are several forms on one page, I want to submit any form without leaving the page. At first, this requirement really bothered me for a few minutes, after reading some documents from jQuery and Merb, it turns out pretty easy to implement:

1. View - file which have the forms you want to submit
In your form.html.erb file, you can create them like the normal forms.
<% @pos.each |purchase_order| do %>
<%= form_for(purchase_order, :action => url(:create_po_purchasing_shopping_lists)) do %>
<%= hidden_field :name => "counter", :value => count %>
<%= partial 'purchasing/purchase_orders/basic_form', :po => po %>
<button class="positive" type="submit">
<img alt="create" src="/images/icons/tick.png"></img>
Create
</button>
<% end =%>
<% end %>
view raw form.html hosted with ❤ by GitHub

2. Javascript(jQuery)
Here is the javascript to do the unobtrusive form submit.
<script type="text/javascript" >
jQuery.ajaxSetup({
'beforeSend': function(xhr) {xhr.setRequestHeader("Accept", "text/javascript")}
});
jQuery.fn.submitWithAjax = function() {
this.submit(function() {
$.post(this.action + '.js', $(this).serialize(), null, "script");
return false;
})
return this;
};
$(document).ready(function() {
$("input").css({backgroundColor: "#FFFFFF"});
$("input").focus(function(){$(this).css({backgroundColor: "#FBE3E4"});});
$("input").blur(function(){$(this).css({backgroundColor: "#FFFFFF"});});
$("form").submitWithAjax();
});
</script>

3. Controller
In your controller's action, you have to specify provides :js
def create_po
provides :js
po = params[:purchase_order]
# do the creating job for the po
render
end
view raw controller.rb hosted with ❤ by GitHub

4. View - file which does the render
This is the good part I like Merb, you can mix Ruby code and javascript together to operate the DOM.
<% if @purchase_order %>
$("div.remote_<%= @counter %>").text("done");
<% else %>
<% if @params['vendor_name'] == "Enter the vendor's name" || @params['purchase_order'].nil? %>
$("#vendor_name").focus();
$("#error_<%=@counter%>").after('<span class="error">Vendor is required.</span>');
<% end %>
<% @params['cb3_ids'].each do |cb3_id| %>
<% %w{quantity unitsize price bt tcost}.each do |word| %>
<% if @params[word + '_' + cb3_id].to_f < 0 %>
$("input[name='<%= word %>_<%= cb3_id %>']").focus();
$("span.error").remove();
$("#error_<%=@counter%>").after('<span class="error"><%= word %> should > 0.</span>');
<% end %>
<% end %>
<% end %>
<% end %>


It is pretty easy and handy.

No comments: