Using Prototype’s built-in JSON and exception handling
March 1st, 2006 Author: Lindsey Simon
Hello Prototype
So I just svn updated my scriptaculous directory and lo and behold there’s a new testing 1.5 version of Prototype in the lib directory. Yum. I’d seen the evalJSON method in the Request before, but not until now did I really want to take advantage of it. In our application, I’m not really using the XmlHttp request to send back large, complex objects to the browser, so 9 times out of 10 some smallish, boolean-oriented response from the server is aok with me, though I’d like to reserve the right to do more sometimes. In other words, the speed-hit from doing an eval on the return string is not something I’m worried about.
Hello JSON
When we inherited the skeleton of our application from Handwire design, it came with two Javascript classes for handling XmlHttpRequests. Their XmlConnector was fairly equivalent to prototype’s Ajax.Request and the HtmlConnector is like Ajax.Updater. The XmlConnector had built in the concept of taking the return (as XML) and parsing it into an object for javascript. Since this return object is both pretty small and pretty simple, it seems like a perfect case to switch over to JSON instead of XML and ditch the 20-line XML DOM parsing routine.
PEAR Services_JSON
Since it exists, I thought I should use Michal Migurski’s Services_JSON PEAR class. I created my own wrapper class called ArrayToJSON which pretty much just adds some headers and spits out the string of JSON.
evalJSON() in Ajax.Request
That JSON string is cool as responseText, but prototype can do something even better with it – if the returned page contains the X-JSON header with the string as its value too. So in my ArrayToJSON class, when I get to the part where I pass out the headers via PHP, I have one extra line:
header("X-JSON: $output");
So I spit out the JSON both in the headers and in the page content. I left it in the page content for readability. This of course means the returned packet is that much bigger, but I’m not concerned. It’s still really quite small and this could always become conditional later.
Note: I had to stick some parentheses around the $json->encode() line since prototype’s evalJSON method doesn’t enclose the value in parentheses the way many recommend for evalling. So now, my return looks like this:
({“success”:true})
and then when I call Ajax.Request, I can set onComplete to a function which will then get two parameters:
exampleOnComplete: function( transport, json )
{
alert( 'yeeha: '+ json.success );
}
Error Handling
Well, I tried to set up what looks like a very elegant way of setting global called functions via prototype, by doing (I’m not showing them, but I’d created some functions called Ajax.onException, Ajax.onFailure etc.. ):
Ajax.Responders.register({
onException: Ajax.onException,
onFailure: Ajax.onFailure,
onSuccess: Ajax.onSuccess
});
But I don’t seem to be getting the call order I expect, so instead I’ve created separate objects which I use when I want to initiate Ajax request or updater calls and then set those in that object, like so:
var AjaxRequest = function( obj )
{
if ( !(obj.parameters && obj.onComplete) ){
top.location.href = "?module=error&error_code=2031&" + "error_message=" + escape("AjaxRequest failed to initialize");
return;
}
// we add on a parameter to break cache and so we know we're dealing with AJAX on the serverside
var random_num = Math.round( ( Math.random() * 666 ) );
new Ajax.Request( '?', {
method: obj.method ? obj.method : "get",
parameters: obj.parameters + "&AJAX=Ajax.Request_"+random_num,
onSuccess: Ajax.onSuccess,
onComplete: obj.onComplete
onFailure: Ajax.onFailure,
onException: Ajax.onException
} );
}
You’ll see there’s an additional parameter as well to all outbound Ajax requests(Ajax.Request=randomnum) – that is so that on the server side, I’ll know which requests are coming via Ajax, and the random number ensures a cache break. I thought about using that strange _= parameter that prototype lops onto the end of every request, but found a blog post that mentioned that the underscore thing is actually a fix for a Safari bug and I expect it’ll disappear at some point anyhow.
My plan is to take advantage of the order of the callbacks to use onSuccess to test for an error on the serverside, and at last onComplete to pass to an onEvent function that actually can do the secondary work, assuming a successful transaction. This is nice because then I’m writing code without having to always write in a conditional to check for errors and consequently call an error function; error checking is now handled exactly the same way for every call.
Conclusion
I’d love to see how other people are implementing prototype’s JSON and error handling mechanisms into their applications. There’s not a great deal of good examples or documentation online about how one should go about doing this, and what’s on this page represents my first crack at it and a few hours of research. I’ve updated the DataGrid code to version 0.2 from an earlier post – now the grid uses this functionality instead of the XmlConnector and HtmlConnector , and you can see that in action here:
Entry Filed under: User Interface
2 Comments Add your own
1. CSwiki站长日志 »&hellip | August 24th, 2006 at 9:09 am
[...] 正在学习使用中…… http://development.finetooth.com/?p=7 http://particletree.com/features/preloading-data-with-ajax-and-json/ [...]
2. Gonzalo Middleton | October 27th, 2006 at 3:36 pm
Regarding: “Note: I had to stick some parentheses around the $json->encode() line since prototype’s evalJSON method doesn’t enclose the value in parentheses the way many recommend for evalling.”… If, on the server side, you change the return content type to “application/json”, you don’t need to add the parenthesis.
Leave a Comment
Some HTML allowed:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>
Trackback this post | Subscribe to the comments via RSS Feed