ProblemContributed by: Diego Scataglini Although Ajax makes web applications more responsive, some operations just take time. Users hate nothing more than an application that appears to be dead while it's sitting there, thinking. To make your application feel more responsive, you want to provide a progress indicator that appears and disappears whenever an Ajax request starts and stops. SolutionFor this recipe, create an empty Rails application. Next, create a basic HTML file for your application's layout. Make sure to load the prototype, effects, and application JavaScript files: app/views/layout/application.rhtml: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>Rails Cookbook</title> <%= javascript_include_tag 'prototype' %> <%= javascript_include_tag 'effects' %> <%= javascript_include_tag 'application' %> </head> <body> <%= yield %> </body> </html> Add the following to your application.js file: public/javascripts/application.js: Ajax.Responders.register({ onCreate: function(){ if($('ajax_busy') && Ajax.activeRequestCount > 0){ Effect.Appear('ajax_busy', {duration: 0.5, queue: 'end'}); } }, onComplete: function(){ if($('ajax_busy') && Ajax.activeRequestCount == 0){ Effect.Fade('ajax_busy', {duration: 0.5, queue: 'end'}); } } }); Now find or create an animated GIF like browsers use to indicate a page is loading. Name this file myspinner.gif and save it in the public/images folder. Now create a helper that outputs the HTML for the progress indicator. This helper lets you re-use the same HTML in all of your views. app/helpers/application_helper.rb: def show_spinner content_tag "div", "Working... " + image_tag("myspinner.gif"), :id => "ajax_busy", :style => "display:none;" end Add a style for the ajax_busy div tag: public/stylesheets/display.css: #ajax_busy {position: absolute; top: 0; right: 0; width: 120px; background-color: #900; color: #fff; padding: 4px;} To test the progress indicator, create a controller with two actions: one to simulate a long-running task and one from which to call it using Ajax: $ ruby script/generate controller Home index myajax_call Next, add the following code to the generated controller file: app/controllers/home_controller.rb: class HomeController < ApplicationController def index end def myajax_call sleep 3 # sleep for 3 seconds render :update do |page| page.alert('I am done sleeping.') end end end Create a view for the index action from which to test the Ajax call: app/views/home/index.rhtml: <%= show_spinner %> <%= link_to_remote "Test Spinner", :url => {:action => "myajax_call"} %> You're done. Start the development server to test your application: $ ruby script/server -d Finally, point your browser to http://localhost:3000/home, and click on the Test Spinner link to see the progress indicator in action. DiscussionThe solution uses the Ajax.Responders object to register a couple of event handlers. Because the Prototype JavaScript library raises events with Ajax.Responders.dispatch, any events generated by Prototype are sent to every registered responder stored in Ajax.Responders.responders. This hook provides a handy way to create a global progress indicator. |