Instant Shiny Forms with Rails and YUI

This tutorial will demonstrate how to convert standalone Ruby on Rails forms to a YUI dialog (hey, it’s one less page load) in only 4 easy steps.

We’ll start with a quick and dirty (and all scaffold) blogging app. I assume you know a thing or two and don’t need me to walk through this. You’ll note that there are no changes required to your controllers to make this work.

Step 1: Include YUI and all of its goodness.

There are a number of tutorials out there for this — Stuart Grimshaw has one method in this article , but an easy way is to use the YUI configuration tool and just serve the files from their CDN — it’s probably faster than your servers, anyway. I prefer to include the files I need, so that javascript_include_tag can at least concatenate them down to one file — which your users will then cache. For this example, I’m going to just do a conventional form submit, rather than an Ajaxy form handler — it doesn’t work with the Prototype.js Ajax class, and Yahoo Connection Manager is a subject for another post.
The basic HTML to include for the YUI container module (which includes Dialog and some other goodness) is:

<!-- css -->
<link rel="stylesheet" type="text/css" href="">
<link rel="stylesheet" type="text/css" href=""> 

<!-- js -->
<script type="text/javascript" src=""></script>
<script type="text/javascript" src=""></script> 
<script type="text/javascript" src=""></script> 
<script type="text/javascript" src=""></script> 
<script type="text/javascript" src=""></script> 
<script type="text/javascript" src=""></script>

You will also want to add a class=”yui-skin-sam” to the body tag in your layout, so you pick up the styling correctly. If you want your own, go for it.

Step 2: Extract your form to a partial.

Of course, you already do this, so #new and #edit use the same partial, right? If not, go ahead and do that.

Step 3: Include the _form partial in the view

You may need to make a couple of changes to get this two work. The YUI container class uses a standard markup pattern like this:

<div id="my_form_div">
  <div class="hd">Header</div>
  <div class="bd">Body</div>
  <div class="ft">Footer (optional)</div>

We don’t need a footer for Dialog to work — in fact, it will add its own later, so we can leave that out. Insert the partial in the body, and your “Show” view should have something like this:

  <%=h @post.title %>

  <%=h @post.body %>

  <%=h @post.published %>

<%= link_to 'Edit', edit_post_path(@post) %> |
<%= link_to 'Back', posts_path %>

<div id="edit_post_div">
  <div class="hd">Edit Post <%=h @post.title %></div>
  <div class="bd"><%= render :partial => 'form', :object => @post %></div>

And the page should currently render something like this:


Step 4: Wire up the dialog box.

You have a couple of options on where to include the javascript to activate this. The Rails default would be to just stick in a javascript_tag in the view after the form, so it’s available when you need it. The YUI standard would be to create an event listener to wait for the form element to be available and then do what needs to be done. My personal preference is to maintain a library of all of the javascript objects I’m using in an app in application.js, and then include a separate file per controller that sets up the listeners to activate the elements I want. To keep things simple, though, I’ll go with the first option — a javascript_tag block after the form. Code first, explanation later:

(function() {
  function handleSubmit (arg) {
  function handleCancel (arg) {
  var postEditor = new YAHOO.widget.Dialog('edit_post_div',
    { width               : "40em",
      fixedcenter         : true,
      visible             : false,
      constraintoviewport : true,
      modal               : true,
      postmethod          : "form",
      buttons : [ { text:"Submit", handler:handleSubmit, isDefault:true},
                  { text:"Cancel", handler:handleCancel } ]
  YAHOO.util.Event.on('edit_post_link', 'click', function(e) {

So what did we just do there? The Dialog constructor takes two arguments, the id (or HTMLElement reference) of the wrapper div, and a configuration hash. The config hash sets up a 40em-wide dialog box that’s centered in the viewport, initially hidden, can’t be dragged off the screen, and that doesn’t allow you to interact with the rest of the page. To save the (nominal) trouble of dealing with Connection Manager, we’re going to submit the form using a conventional POST (or set postmethod to ‘async’ to do an ajax post). We’ve also set up “Submit” and “Cancel” buttons. The call to render() actually converts the form into the hidden dialog box.

Finally, we set up an onClick listener for the Edit link, which displays the dialog — and gives us something like this:


Further Reading


4 Responses to Instant Shiny Forms with Rails and YUI

  1. […] Shiny Forms with Rails and YUI: That’s the title of Enleitened’s new tutorial that "demonstrate[s] how to convert standalone Ruby on Rails forms to a YUI Dialog (hey, […]

  2. Eric Hedberg says:

    I’m contemplating the followup for this article. Any suggestions?

  3. […] Dialogs with Rails and YUI This tutorial is a followup to Instant Shiny Forms with Rails and YUI, with a bit more of an interesting technique that (I think, at least) really demonstrates where YUI […]

  4. Ronny says:

    Even better: if you move the div with id ‘edit_post_div’ and the javascript for dialog setup to its own page (with render :layout => false in controller), then you can easily use this dialog from any page. Just do an ajax call to this page and insert the result on your page (e.g. remote_function with :update => “id-to-empty-div”)

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: