
This is the fifth chapter in our "How to create an Ajax Library" series of articles. If you haven't already you should read the JavaScript/DHTML Effect class first.
The by far most important thing to create when constructing an Ajax Library is the wrapper around the XMLHTTPRequest object. Ever since Jesse James Garrett created his Ajax: A New Approach to Web Applications essay the 18th of February 2005 the world has been drastically bigger in regards to application development. And all though the very foundation of this "growth" was the construction of the XMLHTTP object from a Microsoft employee back in 2001, it was Jesse James (isn't the cowboy association great;) who really created the spin by giving Ajax its name.
In fact that one essay by Jesse James within weeks of writing completely changed the entire known world for developers. If you follow the creation of Ajax libraries you will see that the number of Ajax libraries created *exploded* after the 18th of February 2005. For developers that date probably should be holy or a vacational day or something since by giving Ajax its name Jesse completely changed the world.
And even the name XMLHTTPRequest was a JOKE. The Microsoft employee who created that XHR object was creating it because he needed it himself in his Outlook Web Access software and he needed a name which had something to do with XML since Microsoft where at that time rolling out a Service Pack to MSXML and the only way he could get his XHR object out to the masses was by adding up an "XML" part on the name so that it would be approved for the MSXML Service Pack. So the XMLHTTPRequest has as much to do with XML as Silverlight have to do with web.
But enough history lessons and back to the important stuff. The XMLHTTPRequest object which often is written as XHR for shorthand is the very glue that made this whole revolution possible.
What is the XHR object anyway?
The XHR object is what makes it possible for us to "call into" the server without posting the entire webform back to the server. So with the XHR object we can effectively call into the server, retrieve data from it and do some manipulation on the client (browser) with that retrieved data. The form of the data can be completely self defined and arbitrary, and if anything the XML parts of Ajax probably should be renamed to "XMLHTTPRequest" since extremely few Ajax libraries actually return XML from the server. But that doesn't really matter anymore since most Ajax developers (including me) agree on that Ajax is no longer an "acronym" but rather a self sustained word which can be seen by the fact that most Ajax developers don't even use capital letters for anything else than the initial "A". If it was an acronym it should have been written; "AJAX".
With the XHR object you can create HTTP requests back to the domain the webpage itself is hosted on and then from the server you can return any "data" or even JavaScript for that matter which will be executed on the client.
Let us take a look at some code that wraps the XHR object for us;
Ra.XHR = Ra.klass();
// True if an ongoing request is in progress
// Ra.XHR does not allow more than one active request at the time...
Ra.XHR.activeRequest = false;
Ra.extend(Ra.XHR.prototype, {
init: function(url, options) {
this.initXHR(url, options);
},
initXHR: function(url, options) {
if( Ra.XHR.activeRequest ) {
throw 'Cannot have more than one active XHR request at the time...';
}
Ra.XHR.activeRequest = true;
this.url = url;
this.options = Ra.extend({
onSuccess: function(){},
onError: function(){},
body: ''
}, options || {});
this.start();
},
start: function(){
// Getting transport
this.xhr = new XMLHttpRequest();
if( !this.xhr ) {
this.xhr = new ActiveXObject('Msxml2.XMLHTTP');
}
if( !this.xhr ) {
this.xhr = new ActiveXObject('Microsoft.XMLHTTP');
}
// Opening transport and setting headers
this.xhr.open('POST', this.url, true);
this.xhr.setRequestHeader('Accept', 'text/javascript, text/html, application/xml, text/xml, */*');
this.xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
// Setting body of request
this.xhr.send(this.options.body);
// Now we can start checking for readyState (waiting for request to be finished)
var T = this;
this.xhr.onreadystatechange = function() {
if( T.xhr.readyState == 4 ) {
T._finished();
}
};
},
// Called when request is finished
_finished: function(){
if( this.xhr.status >= 200 && this.xhr.status < 300 ) {
this.options.onSuccess(this.xhr.responseText);
} else {
this.options.onError(this.xhr.status, this.xhr.responseText);
}
// Resetting active requests back to false to allow next request to run
Ra.XHR.activeRequest = false;
}
});
The above is a minimalistic implementation of a class that wraps the XHR object. Now to use that class is even easier than to write it, take a look at this code;
new Ra.XHR('RaDOMBasics.aspx', {
body: 'somePostParameter=whatever',
onSuccess: function(response) {
alert(response);
},
onError: function(statusCode, response) {
alert('Something went wrong, response from server was; ' response);
}
});
As you can see above there are basically just four things you need to think about when consuming our XHR class. The first is the URL to where you want to create your request. Notice here though that most browsers stops you from creating requests to any other domain than the domain you are hosting your webpage on. Which means that unless you want to be more creative than what's commonly thought of as "sane" you should NOT try to create Ajax Requests back to anything else than the domain which are hosting your web application.
The second thing of importance is the body optional parameter in which you can add up post parameters to your webpage which it will be posible for you to retrieve back on the server and can be thought of as "function arguments" to your server call. These should be separated by an ampersand (&) if you have multiple arguments and they also should be URI encoded with the encodeURIComponent JavaScript global function if they have non ASCII characters within themselves. Very often you will serialize the FORM element values here.
The third important point here is the "onSuccess" function which will be called when your server request returns. This function will be passed one parameter which is the content returned from the server from which you can use on the browser. Here it is important to notice that the XHR object will call back into your server *ASYNCHRONOUSLY* which means that this is NOT an atomic operation.
Notice that both the onSuccess and the onError methods are not being called in a special context (this pointer) which means that the this pointer in both of those pointers will end up being the options object. If you wish to pass down arbitrage data however to those two methods then an easy "hack" to do so is to extend the options object with your own data/pointers.
About multiple open Ajax Requests at the same time
Most Ajax libraries will allow more than one Ajax request to run at the same time. Since our Ajax library will only allow requests back to the same server as the page is hosted on and allowing multiple open requests open at the same time creates a *LOT* of problems we will NOT allow more than one Ajax request running at the same time.
If you decide to allow more than one Ajax Request running at the same time you should expect to create yourself a lot of problems related to state on the client versus server. Let us say that you create an Ajax Request which serializes the form and post it back to the server and then as a result of that server operation you create change values and/or add/remove form elements on the page. Then if you create a new Ajax Request before that first request has returned which also serializes the form elements, then the values of those form elements will be undefined and unexpected by the server. Due to those kind of problems we will *eliminate* the possibility of having more than one open Ajax Request at any given time and actually throw an exception if one is created before the first has returned. This will at the cost of a little bit extra coding massively reduce the amount of headache for both ourselves and the users of our Ajax library.
In another class which we will describe later we will implement "queueing" of our Ajax Requests so that if you create an Ajax Request while another Ajax Request is currently active, then the second Ajax Request will be "stalled" and put into a queue waiting for the first to finish before it is being executed. But this is not really the job of the XHR class due to that it doesn't itself serialize form elements so we will have to wait with that one a couple of chapters.
So that's about it for now, until next time, have a nice day :)
Thomas Hansen