Hack 51. Update an HTML Element's Content from the Server
Use Prototype's Ajax.Updater object to easily update web page content with server information. Prototype comes with its own object that uses a few lines of code to update a subset of a web page with server data. This hack presents the user with the web page that Figure 6-1 shows, plus a new Go Updater! button. It is similar to "Use Prototype's Ajax Tools with Your Application" [Hack #50], except that it uses a different type of Prototype Ajax object to update an HTML element's content in the web page. When the user clicks the Go Updater! button, the code uses the Ajax.Updater object to specify that the textarea should be updated with the text from the server response. Figure 6-3 shows what the web page looks like after the user clicks the button. Figure 6-3. Updated content with little programmingThe first order of business for this hack is to make sure that the web page imports the prototype.js library, which you can download from http://prototype.conio.net. Place this file in a common directory for JavaScript, then use a script tag to import the file into the web page: <script src="/books/4/254/1/html/2//javascripts/prototype.js" type="text/javascript"></script> <script src="/books/4/254/1/html/2//javascripts/mylib.js" type="text/javascript"></script> The second imported library, mylib.js, is where the hack uses the objects and extensions from the Prototype package. Here's the web page code that shows the various user interface controls that this hack depends on, including the Go Updater! button and the textarea that receives server data: <html> <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/mylib.js" type="text/javascript"></script> <title>Using Prototype Ajax</title> </head> <body> <h3>Please enter your name</h3> <form action="javascript:void%200" method="get"> <p> Your first & last name: <input type="text" name="name_info" id= "name_info" size="20" maxlength="25" value="anonymous user"> </p> <p> Age group: <select name="ag" > <!options part snipped...--> </select> </p> <p> <span style="font-size:1.2em; color: green"></span> </p> <button type="button" name="but1">Go Updater!</button> <p> <textarea name="display_area" rows="30" cols="30"> </textarea> </p> </form> </body> </html> The purpose of the hack is to automatically fill the textarea with backend data at the user's urging, using just a handful of code lines: window.onload=function( ){ if($("but1")) { $("but1").onclick=function( ){ if($F("name_info")){ _url="http://localhost:8080/hacks/proto"; showUpInfo(_url); } } } }; function showUpInfo(go_url){ if($("display_area") && go_url){ var xmlHttp= new Ajax.Updater("display_area",go_url, { parameters:Form.serialize(document.forms[0]), onComplete:function(request){ if(xmlHttp.responseIsFailure( )) { var sts = xmlHttp.transport.status ? xmlHttp. transport.status : "undefined"; $("display_area").value= "XMlHttpRequest returned response status "+sts; document.getElementById("msg").innerHTML= "HTTP response and server information; "+ "response status="+ request.status; } else { $("display_area").value=request.responseText; document.getElementById("msg").innerHTML= "Server information fetched with Ajax.Updater:"+ "status="+request.status; } } }); } } When the browser loads the web page (window.onload), the button's onclick event handler points to the showUpInfo( ) function. This is the techie way of explaining that when the web page loads, the JavaScript will define the button's behavior. This behavior includes checking whether the button with id but1 actually exists (a web page designer could have mistakenly left it out).
On the Server SideshowUpInfo( ) creates a new Ajax.Updater object, passes some information into its constructor, and that's it: the request is on its way. The code didn't have to fuss with XMlHttpRequest because this object was wrapped and tucked away in the prototype.js file. The Ajax.Updater object is different from Ajax.Request, which the previous hack deals with, because its first parameter is the id of the page element that is updated with server data. In this case, the code passes in the id of the textarea. Now let's look at what's happening at the server end. This application is running on Ruby on Rails. The URL http://localhost:8080/hacks/proto (the second parameter inside of Ajax.Updater) posts the web page's data to an action called proto.
Here is how the action is defined in Ruby code: def proto if @request.xml_http_request?( ) #en is a hash type in Ruby #en["SERVER_SOFTWARE"]returns the vlaue of the #SERVER_SOFTWARE environment variable en=@request.env( ) str="Server: " str+=en["SERVER_SOFTWARE"].to_s+"\\n" str+="Query string: "+en["QUERY_STRING"].to_s+"\\n" str+="Raw post data: "+en["RAW_POST_DATA"].to_s+"\\n" str+="Prototype version: "+en["HTTP_X_PROTOTYPE_VERSION"].to_s+"\\n" str+="X_REQUESTED_WITH header: "+ en["HTTP_X_REQUESTED_WITH"].to_s +"\\n\\n" render :text => str end end This is a method that gathers environment variable information related to the request, then sends it back to the requester. An action in Ruby on Rails assumes the same role as a servlet or JSP in Java (see "Dynamically View Request Information for XMLHttpRequest" [Hack #62]). The proto action is designed to send this information only if the request originates from XMLHttpRequest, as discussed in "Find Out Whether Ajax Is Calling in the Request" [Hack #59]. It does not make sense to make this action available to a typical browser request because its return value is meant only for fine-grained web page updates.
Checking for ErrorsThe code displays an error message if the HTTP response involves status codes such as 404 (Not Found), 500 (Server Error), or 503 (Service Unavailable): onComplete:function(request){ if(xmlHttp.responseIsFailure( )) { var sts = xmlHttp.transport.status ? xmlHttp. transport.status : "undefined"; $("display_area").value= "XMlHttpRequest returned response status "+sts; document.getElementById("msg").innerHTML= "HTTP response and server information; response status="+ request.status; } else {//...continued |