Ajax Hacks: Tips & Tools for Creating Responsive Web Sites
Display the status of your XMLHttpRequest remote calls in a Ruby on Rails application. When debugging applications, it is handy to see the status of your remote request as the response handling unfolds. This hack displays the request status in a Ruby on Rails view before it displays the return value. The user sees the status message without a page refresh or rebuild. Developers do not have to explicitly deal with XMLHttpRequest at all, because the hack uses a method that is part of the RoR framework to implement the remote calls.
Like the other hacks in this chapter, this one runs within a Ruby on Rails application. Figure 7-5 shows what the hack looks like in the Safari browser. Figure 7-5. Gentlemen, start your monitors
When the user clicks the Start Monitor button, the application sends a request to a server component, called an action in Rails parlance. This action returns some content to update a div in the web page, without refreshing the page itself. As Figure 7-6 depicts, the page shows the request status at each of its stages, in real time as it's happening. Figure 7-6. Display the request's status in real time
The code for the Rails template associated with this action is shown below. It's very similar to the code for an HTML page, but the template has an .rhtml suffix, as in monitor.rhtml. This view name is mapped to the URL the user enters in the browser: http://localhost:3000/hacks/monitor.
Here's the template code: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-strict.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" /> <%= javascript_include_tag :defaults %> <title>Monitor Ajax calls</title> </head> <body> <%= form_remote_tag(:update => "complete",:url => { :action => :zero_update }, :position => "top", :success => "$('success').innerHTML='Success; request status='+request.status", :loading => "$('loading').innerHTML='Loading...'", :loaded => "$('loaded').innerHTML='Loaded; request status='+request.status", :interactive => "$('inter').innerHTML='Interactive; request status= '+request.status", :failure => "$('failure').innerHTML='Failure; request status='+request.status") %> <h3>Monitor Your Ajax Calls with Rails</h3> <div ></div> <div ></div> <div ></div> <div ></div> <div ></div> <div ></div> <p> <%= submit_tag "Start Monitor" %> </p> <%= end_form_tag %> </body> </html> The RoR views use embedded Ruby script tags ( <%...%>), similar to the tags used in JSP or PHP applications. One of these tags contains a Rails method, form_remote_tag( ). This method takes care of all the XMLHttpRequest initializing and sending for you, so you can focus on the behavior you want your applications to initiate. The parameters for form_remote_tag( ) are fairly dense, but they accomplish an awful lot beneath the surface. First, the :update parameter specifies the id of the page element (a div, in this case) that the action will update with the request's return value. Next, the :url parameter points to the name of the action that will handle the request (here, zero_update): :url => { :action => :zero_update }
This is similar to other mapping mechanisms used by web development APIs such as Java Servlets (see the further explanation below). The rest of the method parameters specify the JavaScript code that the application should execute at each stage of the request's processing: :success => "$('success').innerHTML='Success; request status='+request.status", :loading => "$('loading').innerHTML='Loading...'", :loaded => "$('loaded').innerHTML='Loaded; request status='+request.status", :interactive=>"$('inter').innerHTML='Interactive; request status='+request.status", :failure => "$('failure').innerHTML='Failure; request status='+request.status"
For example, when the request object's readystate property equals loaded (it's actually a numerical value of 2; see Chapter 1), this part of our parameter specifies what will happen with the application: :loaded => "$('loaded').innerHTML='Loaded; request status='+request.status" The code will get a reference to an HTML element with an id of loaded. The code does so using a shortcut included in the prototype.js package, which this page imports: $('loaded'). This JavaScript code is the equivalent of document.getElementById('loaded'), but it sure is easier to type! With the reference to that Element object, the code sets its innerHTML property to Loaded; request status= plus the status code returned by the HTTP response. If All Else Fails
What if the request fails? Figure 7-7 shows what the displayed message looks like when you add code to the server component to raise a response error. Figure 7-7. Displaying a response status signifying failure
The return value is a bit of HTML announcing a problem, along with an HTTP response status code of 500. The XMLHttpRequest object connects with a server component, or action, named zero_update. As described in "Install Ruby on Rails" [Hack #55], Rails explicitly uses a MVC architecture in the way that it sets up the directories for your web application. If your RoR URL is http://localhost:3000/hacks/monitor, the controller component is in the controllers directory at the following path: <app-root>/hacks/controllers/hacks_controller.rb. Let's take a look inside hacks_controller.rb for the definition of the zero_update action. Controller objects are written in Ruby code. To generate an action, all you have to do is define a method inside the controller class (literally, a class that extends ActionController, which is part of the Rails API) with the action's name: class HacksController < ApplicationController def index #defined in index.rhtml end def zero_update render :text => "Ajax return value..." end #rest of class... end
Complicated, huh? All the zero_update action does is return the specified text in the HTTP response using the Rails method render( ).
The request object handles the response behind the scenes, placing its content within the div with id complete. However, all the developer is responsible for is calling the method: you do not have to touch the request object or its response handlers. It's useful to have a look at the source code that Rails returns when the user requests the monitor.rhtml template: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-strict.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" /> <script src="/books/4/254/1/html/2//javascripts/prototype.js" type="text/javascript"></script> <script src="/books/4/254/1/html/2//javascripts/effects.js" type="text/javascript"></script> <script src="/books/4/254/1/html/2//javascripts/dragdrop.js" type="text/javascript"></script> <script src="/books/4/254/1/html/2//javascripts/controls.js" type="text/javascript"></script> <script src="/books/4/254/1/html/2//javascripts/application.js" type="text/javascript"></script> <title>Monitor Ajax calls</title> </head> <body> <form action="/hacks/zero_update" method="post" onsubmit="new Ajax.Updater('complete', '/hacks/zero_update', {asynchronous:true, evalScripts:true, insertion:Insertion.Top, onFailure:function(request){$('failure').innerHTML='Failure; request status='+request.status}, onInteractive:function(request){$('inter').innerHTML='Interactive; request status='+request.status}, onLoaded:function(request){$('loaded').innerHTML='Loaded; request status='+request.status}, onLoading:function(request){$('loading').innerHTML='Loading...'}, onSuccess:function(request){$('success').innerHTML='Success; request status='+request.status}, parameters:Form.serialize(this)}); return false;"> <h3>Monitor Your Ajax Calls with Rails</h3> <div ></div> <div ></div> <div ></div> <div ></div> <div ></div> <div onclick="clearIt($('complete'))"></div> <p> <input name="commit" type="submit" value="Start Monitor" /> </p> </form> </body> </html> Notice all the script tags embedded in the HTML source. This was made possible by the <%= javascript_include_tag :defaults %> embedded method (see "Make Your JavaScript Available to Rails Applications" [Hack #57] for details on that one).
The imported JavaScript files include prototype.js, and the Prototype library just happens to include the Ajax.Updater JavaScript object that wraps the initializing and use of XMLHttpRequest. (See Chapter 6 for more details on this package.) So there you have itthe form_remote_tag( ) method represents a wrapper enclosing another wrapper, which handles the request object automatically. |
Категории