Saturday, 23 September 2006

let's get modal

Note: The RichText blog has moved to

UPDATE 12 July 07: Check out this more recent post for more info on modal dialogs.

In the Ruby On Rails project on which I'm currently working, I felt the need to introduce the concept of a modal dialog box. This is a common requirement in traditional client-applications, in order to restrict a user's actions to a certain area of the screen. However, until the advent of Web 2.0 it has been tricky implement in internet-based applications.

I tried a few different approaches, but finally settled on using the Xilinus Prototype Window javascript libraries to help me out.

A bit of background...

In my project, I orginally had a table which contained links to allow the user to edit the (often complex) contents of some of the table-cells. The links would causethe user to be navigated away from the page, and so after they had finished doing their editing they would have to navigate back. This is all very well, apart from the fact that if we wanted the site to remember other values that the user had entered on the first page, it meant storing hoards of things in the session. I stuck with this approach for a while, but the more functionality I added to the page, the more work I had to put into maintaining state in the session. Something had to change!

My solution

1. First download the Prototype Window libraries from here. (I used version 0.96.2).

2. The download zip-file contains a bunch of folders. Put the window.js file from the javascripts folder into your rails project's public/javascripts folder. (You will also need the prototype.js file, but this should already be there for you if you generated your project from a skeleton). The stylesheets folder in the zip-file contains all the different themes that are demonstrated on the xilinus prototype window samples page. The styles that you want to use need to go into your rails project's public/stylesheets folder. (I made a window subfolder to keep things tidier).

3. So that your views can use the prototype window javascript, put a javascript include tag in the head of the appropriate layouts file. For the sake of this example, put it in the application.rhtml layout file.

<%= javascript_include_tag "prototype", "effects", "window" %>
Also put in a stylesheet link to the style of modal dialog log you want.
<%= stylesheet_link_tag "/stylesheets/window/default" %>
We're going to use some javascript later, so put a content_for_page_scripts tag in the head of the layout too.
<script type="text/javascript"><%=@content_for_page_scripts %></script> 

4. In the view from which you wish to launch the modal dialog, we need some javascript. Write a function, and put it inside a content_for page_scripts block. Here, the function launches a modal dialog containing the web page at the url passed in.
<% content_for("page_scripts") do -%>

function show_Array_Window(myUrl)
  win = new Window('window_id', {title: "Array", width:300, height:500, url:myUrl});

<% end -%>

5. Now set up a link for the user to click on, and set the onclick event to the function we just wrote. In the example below, we're building up a table with a link to a different object in each row's cell. (If you just want a permanent link to a single modal dialog, you dont need to go to the trouble of building up a dynamic onclick string).
  <% for i in 0...@my_array.length do %>
          # other code here if you like
          "<a href = \"#\" onclick='show_Array_Window(
\"" + url_for(:controller => 'my_controller',
:action => 'my_Action', :id => i) + "\")'>
Go Modal!</a>"
  <% end %>

6. You can now do whatever you want in the page that's shown. In my case, this involved allowing the user to edit the complex data structure that was represented by the object in the table.

... and that's it.

The advantage of using the Xilinus Prototype Window libraries over other modal dialog approaches like lightbox gone wild is that because you just point the dialog to a url, the page that is shown inside can include its own javascript. This allows you to do ajax stuff inside your dialog like it was any other rails page. (I wont go into the whole ajax thing here).

Digg Technorati Stumbleupon Reddit Blinklist Furl Spurl Yahoo Simpy

Please also visit the Swirrl blog


Michael Kovacs said...

Hey Rich,

Nice writeup. I looked at using this library as well but if I remember properly it wasn't as flexible as I wanted and the LBGW library was closer to what I wanted OOTB. But one correction, with LBGW all you do is point at a URL as well, it's just that your link has a class associated with it that registers an interceptor to create/show the div with the contents of the URL. So you're still able to put javascript and whatever you like. For me I wanted to accomplish the same thing you did but the place where I had difficulty was trying to make the dialog dismiss after my AJAX form submission. I ended up using an RJS template for the purpose, I'm curious what you ended up doing.


Rich said...

Thanks for your comment, Michael. I'll post another entry soon, with details of the ajax stuff.

Rich said...

I recently noticed that the html code in this post was not formatted correctly. It should be sorted now. Apologies.

Matt said...

Thanks for the great writeup. I was able to get everything working just as you suggested. However, I'd be curious to know how you handled the dialog dismiss after the AJAX form submission while being able to capture error messages and/or if you have any intent to post an entry on the subject as you suggested earlier.


Rich said...

Check out my post in July 07, which follows up on some of this stuff.