AJAX and PHP

June 7, 2010

AJAX

«»

XMLHttpRequest, version 2


As in this book we do our best to combine theory and practice, before moving on to
implementing the AJAX form validation script, we’ll have another quick look at our
favorite AJAX object—XMLHttpRequest.


On this occasion, we will step up the complexity (and functionality) a bit and use
everything we have learned until now. We will continue to build on what has come
before as we move on; so again, it’s important that you take the time to be sure
you’ve understood what we are doing here. Time spent on digging into the materials
really pays off when you begin to build your own application in the real world.


In Chapter 2, we took a sneak peak at the XMLHttpRequest object—the nexus of
the AJAX world. Back then, we didn’t have any OOP JavaScript skills. We’ve seen
the power hidden in JavaScript in Chapter 3. In Chapter 4, we saw how PHP works
together with AJAX requests.


Our OOP JavaScript skills will be put to work improving the existing script that used
to make AJAX requests. In addition to the design that we’ve already discussed, we’re
creating the following features as well:



  • Flexible design so that the object can be easily extended for future needs
    and purposes

  • The ability to set all the required properties via a JSON object


We’ll package this improved XMLHttpRequest functionality in a class named
XmlHttp that we’ll be able to use in other exercises as well. You can see the class
diagram in the following screenshot, along with the diagrams of two helper classes:



  • settings is the class we use to create the call settings; we supply an instance
    of this class as a parameter to the constructor of XmlHttp

  • complete is a callback delegate, pointing to the function we want executed
    when the call completes


The final purpose of this exercise is to create a class named XmlHttp that we can
easily use in other projects to perform AJAX calls. This class is an improvement of
the async.js script that you built in Chapter 2, JavaScript and the AJAX Client.



With our goals in mind, let’s get to it!


Time for action – the XmlHttp object



  1. In the ajax folder, create a folder named validate, which will host the
    exercises in this chapter.

  2. In the validate folder, create a new file named xhr.js and add the
    following code to it:


  3. // XmlHttp constructor can receive request settings:
    // url – the server url
    // contentType – request content type
    // type – request type (default is GET)
    // data – optional request parameters
    // async – whether the request is asynchronous (default is true)
    // showErrors – display errors
    // complete – the callback function to call when the request
    // completes
    function XmlHttp(settings)
    {
    // store the settings object in a class property
    this.settings = settings;
    // override default settings with those received as parameter
    // the default url points to the current page
    var url = location.href;
    if (settings.url)
    url = settings.url;
    // the default content type is the content type for forms
    var contentType = “application/x-www-form-urlencoded”;
    if (settings.contentType)
    contentType = settings.contentType;
    // by default the request is done through GET
    var type = “GET”;
    if(settings.type)
    type = settings.type;
    // by default there are no parameters sent
    var data = null;
    if(settings.data)
    {
    data = settings.data;
    // if we go through GET we properly adjust the URL
    if(type == “GET”)
    url = url + “?” + data;
    }
    // by default the postback is asynchronous
    var async = true;
    if(settings.async)
    async = settings.async;
    // by default we show all the infrastructure errors
    var showErrors = true;
    if(settings.showErrors)
    showErrors = settings.showErrors;
    // create the XmlHttpRequest object
    var xhr = XmlHttp.create();
    // set the postback properties
    xhr.open(type, url, async);
    xhr.onreadystatechange = onreadystatechange;
    xhr.setRequestHeader(“Content-Type”, contentType);
    xhr.send(data);
    // the function that displays errors
    function displayError(message)
    {
    // ignore errors if showErrors is false
    if (showErrors)
    {
    // display error message
    alert(“Error encountered: \n” + message);
    }
    }
    // the function that reads the server response
    function readResponse()
    {
    try
    {
    // retrieve the response content type
    var contentType = xhr.getResponseHeader(“Content-Type”);
    // build the json object if the response has one
    if (contentType == “application/json”)
    {
    response = JSON.parse(xhr.responseText);
    }
    // get the DOM element if the response is XML
    else if (contentType == “text/xml”)
    {
    response = xhr.responseXml;
    }
    // by default get the response as text
    else
    {
    response = xhr.responseText;
    }
    // call the callback function if any
    if (settings.complete)
    settings.complete (xhr, response, xhr.status);
    }
    catch (e)
    {
    displayError(e.toString());
    }
    }
    // called when the request state changes
    function onreadystatechange()
    {
    // when readyState is 4, we read the server response
    if (xhr.readyState == 4)
    {
    // continue only if HTTP status is “OK”
    if (xhr.status == 200)
    {
    try
    {
    // read the response from the server
    readResponse();
    }
    catch(e)
    {
    // display error message
    displayError(e.toString());
    }
    }
    else
    {
    // display error message
    displayError(xhr.statusText);
    }
    }
    }
    }
    // static method that returns a new XMLHttpRequest object
    XmlHttp.create = function()
    {
    // will store the reference to the XMLHttpRequest object
    var xmlHttp;
    // create the XMLHttpRequest object
    try
    {
    // assume IE7 or newer or other modern browsers
    xmlHttp = new XMLHttpRequest();
    }
    catch(e)
    {
    // assume IE6 or older
    try
    {
    xmlHttp = new ActiveXObject(“Microsoft.XMLHttp”);
    }
    catch(e) { }
    }
    // return the created object or display an error message
    if (!xmlHttp)
    alert(“Error creating the XMLHttpRequest object.”);
    else
    return xmlHttp;
    }


  4. To quickly test the functionality of your XmlHttp class, create a new file
    named xhrtest.html and add the following code to it:


  5. <html>
    <head>
    <script type=”text/javascript” src=”xhr.js”></script>
    </head>
    <body>
    <div id=”test”>
    </div>
    <script>
    XmlHttp
    ({url:’async.txt’,
    complete:function(xhr,response,status)
    {
    document.getElementById(“test”).innerHTML = response;
    }
    });
    </script>
    </body>
    </html>


  6. Now create async.txt with some text in it, and then load
    http://localhost/ajax/validate/xhrtest.html. Figure 5-6 shows
    our result:



What just happened?


The code listed above contains significant code from the previous examples and then
some new code. Let’s break it down into small pieces and analyze it.


The chosen name for our reusable object is XmlHttp. Its functionality is wrapped in
two functions:



  • XmlHttp.create(): A static method of the XmlHttp object that creates a
    XmlHttpRequest object

  • XmlHttp(): The constructor of the XmlHttp object


From a design point of view, the XmlHttp object represents a wrapper around the
XmlHttpRequest object. The XmlHttp.create() method contains the same code that
we have previously seen in the createXmlHttpRequestObject() method. It simply
acts like a factory for an XmlHttpRequest object.


The constructor of the XmlHttp object, although it can look quite scary at first sight,
actually contains very simple code—provided that you know the theory from
Chapter 3, Object Oriented JavaScript. The constructor receives as a parameter a JSON
object containing all the settings for the XmlHttp object. Choosing a JSON object is
both convenient from the extensibility point of view and easy from the programming
point of view. We store the settings in a property with the same name.



function XmlHttp(settings)
{
// store the settings object in a class property
this.settings = settings;


The settings object contains the following properties that will be mainly used for
the XmlHttpRequest object:



  • url: The URL of the AJAX request

  • type: The type of the request (GET or POST)

  • contentType : The content type of the request

  • data: The data to be sent to the server

  • async: A fl ag that specifies whether the request is synchronous or
    asynchronous

  • complete: The function called when the request completes

  • showErrors: A fl ag that indicates whether infrastructure errors will be
    displayed or not


These are the parameters required to make an AJAX request. Even though the
structure and the design of this object are simple, it can be easily extended with
more advanced features, giving us the fl exibility feature we defined as a goal.


The fl exibility offered by JSON objects means we don’t force the user to pass all the
properties mentioned above each time the object is created. Instead, we created a
standard set of default values that the user can choose to overwrite when necessary.
The next few lines simply implement this logic.


Making a request through GET or POST is different and we take care of it when setting
the parameters for the request:



// by default there are no parameters sent
var data = null;
if(settings.data)
{
data = settings.data;
// if we go through GET we properly adjust the URL
if(type == “GET”)
url = url + “?” + data;

}


After having all the settings for the AJAX request, we create the XmlHttpRequest
and we open it.



// create the XmlHttpRequest object
var xhr = XmlHttp.create();
// set the postback properties
xhr.open(type, url, async);


The next step is to hook to the readystatechange event:



xhr.onreadystatechange = onreadystatechange;


The handler function is a inner function of the constructor and contains the same
code as the handleRequestStateChange() method that you already know.


Probably the most interesting piece of code is in the response handler. The
readResponse() inner function is responsible for handling the response received
from the server. It gets the content type of the response and, based on that, it builds
the response JSON object or it retrieves the response as an XML element. If no
matching content type is found, the raw text of the response is used instead.



// retrieve the response content type
var contentType = xhr.getResponseHeader(“Content-Type”);
// build the json object if the response has one
if (contentType == “application/json”)
{
response = JSON.parse(xhr.responseText);
}
// get the DOM element if the response is XML
else if (contentType == “text/xml”)
{
response = xhr.responseXml;
}
// by default get the response as text
else
{
response = xhr.responseText;
}


After gathering the necessary data, the XmlHttp object passes it all to the callback
function (settings.complete()) along with the XmlHttp object and the HTTP
response code.



// call the callback function if any
if (settings.complete)
settings.complete (xhr, response, xhr.status);


All in all, the next time you need to call a server script asynchronously from a web
page, you can count on XmlHttp to do all the dirty work. You just tell it what URL to
contact, specifying the necessary parameters, and it fetches the response for you.

email

«»

Comments

comments