Articles: 843 | Categories: 148   
   
   
Home Articles Contact Us
 
 
 
 
Ajax with Prototype (0 Comments)
Admin: Posted Date: April 4, 2010

In addition to all of the other useful classes Prototype gives to developers, it also provides a number of classes and methods for development of Ajax-enabled web applications.

Ajax with Prototype

Introduction

In addition to all of the other useful classes Prototype gives to developers, it also provides a number of classes and methods for development of Ajax-enabled web applications. That is, it allows developers to easily perform HTTP sub-requests using XmlHttpRequest and to handle the response accordingly.

In this article I will show you how Prototype makes Ajax development for developers by covering the functionality it provides. Additionally, I will show you how to easily transfer data between your JavaScript code and hosting server using JSON data.

There are many Prototype concepts and functions used in this example that have been covered in earlier articles in this series, such as selecting and updating elements, and event handling.

In the final article in this series, I will cover an extensive example of programming with Prototype, which will include using Ajax to communicate with the web server.

Request Types

There are three different classes Prototype provides to perform Ajax requests. These are Ajax.Request, Ajax.Updater and Ajax.PeriodicalUpdater.

Ajax.Request

This is the primary class that is used for Ajax, which the other two classes mentioned above use also. Ajax.Request provides a cross-browser solution for performing Ajax requests without requiring that you know how to specifically use XmlHttpRequest.

This class is simple to use – just instantiate it and pass to it the URL you want to send a HTTP request to as the first argument. You can optionally pass a second argument to the constructor which you can use to specify a number of different options. It's pretty rare that you would ever use this class without specifying the second argument, since most of the time you will want to use the response in some way.

Listing 1 shows an example of performing an Ajax request. For now, the options are an empty JavaScript object, but in the next section I will cover the different options that are available for you to specify. These also apply to the Ajax.Updater and Ajax.PeriodicalUpdater classes we will look at shortly.

Note: If you try out this example, your sub-request will likely result in a 404 file not found error since the requested file does not exist. Try changing the URL to a file you know exists on your server.
Listing 1 A basic Ajax sub-request (listing-1.html)
<html>
<head>
<title>A basic Ajax sub-request</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<script type="text/javascript">
var options = {
method : 'post'
}
new Ajax.Request('/path/to/file', options);
</script>
</body>
</html>
Note: Ajax.Request is a class, not a function, so you must remember to instantiate the class by including the new keyword.

In the above example, the options object is used to hold the various request options. This may include get or post data to include in the request or instructions on what to do on successful completion (or failure) of the request. In this example, I have set the request method to be post, simply by specifying the method option.

In this example, once the script is requested nothing will happen since we haven't defined any callbacks for handling the response. I will cover this in the next section.

Ajax.Updater

The Ajax.Updater class is an extension of the normal Ajax.Request class, specifically used to update a DOM element with whatever content is returned from the HTTP sub-request. The alternative would be to use Ajax.Request yourself directly, then manually handle the response and update the required element accordingly.

Listings 2 and 3 show an example of using Ajax.Updater. In Listing 2 is some basic HTML content that we are going to load into the page shown in Listing 3.

Listing 2 Sample content to load using Ajax.Updater (listing-2.html)
<p>
Here is some HTML content, with any element you would normally
use, such as <strong>bold</strong> and <a href="http://www.example.com">a link</a>.
</p>
Listing 3 Populating an element using Ajax.Updater (listing-3.html)
<html>
<head>
<title>Populating an element using Ajax.Updater</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div>
<input type="button" value="Click Me!" id="myButton" />
</div>
<script type="text/javascript">
function onButtonClick(e)
{
var button = Event.element(e);
new Ajax.Updater(button.up(), 'listing-02.html');
}
$('myButton').observe('click', onButtonClick);
</script>
</body>
</html>

When you load this code in your browser you will see a button that says "Click Me!". When you click the button, an Ajax request is triggered to request the listing-02.html file. This occurs in the event handler onButtonClick.

The first argument to Ajax.Updater is the element to populate with the results from the Ajax request. The second argument is the URL to fetch. As mentioned previously, we could also specify options to pass as the final arguments. Because this example is somewhat trivial, I have not done so, but you will see how to do this later in this article.

If you're unsure how the event handling code works in this example, please refer to the fourth article in this series.

Ajax.PeriodicalUpdater

The Ajax.PeriodicalUpdater class is somewhat similar to the Ajax.Updater class, except instead of performing a single sub-request and updating an element it will continue to perform sub-requests after a specified period, thereby continually updating the element's content.

Because this class and Ajax.Updater are extensions to the base Ajax.Request class, the remainder of this article will focus specifically on using Ajax.Request.

Request Options

There are a number of different options you can pass to Ajax.Request that allow you to control specifically how the sub-request should be performed, or how its response should be handled. Although there are quite a number of different options.

In order to use these options, you typically create a JavaScript object in which you specify whichever options are required. My own preference is to call this variable options, but it doesn't really matter. You then pass this variable as the second argument to Ajax.Request (the first argument is the URL to request).

Listing 4 shows an example of how to specify the request options to pass to Ajax.Request. My own personal preference is to create the options separately from the call to Ajax.Request, since it makes the code slightly easier to read.

Listing 4 How to specify options to pass to Ajax.Request (listing-4.js)
// method 1: create options separate to request
var url     = '/url/to/request';
var options = {
// specify one or more options here
};
new Ajax.Request(url, options);
// method 2: specify all options inline
new Ajax.Request('/url/to/request', {
// specify one or more options here
});

Now that you know how to create the options, I'll cover the most important options available.

  • method: This specifies the type of HTTP request to perform. The typical values for this are post or get. If you don't specify this option, post is used. As always, if you are sending data back to the server that may affect the state of the application (e.g. updating data in the database) you should use post.
  • parameters: This option specifies any form data you would like to include in your request. This value can be a string or you can pass an object, which Prototype will automatically serialize and escape for you. This is demonstrated below.
  • postBody: If you're performing a request with a method of post, then you can use this option to hold the post data. My own preference is just to use the parameters option, since it will be used for a post request if postBody is left empty.

Listing 5 shows an example of specifying these options for Ajax.Request. Take note of how the parameters option is specified. The second method is preferred since it not only looks cleaner but ensures your values are encoded properly.

Listing 5 Some basic examples of specifying Ajax.Request options (listing-5.js)
// method 1: create parameters as a string
var url     = '/url/to/request';
var options = {
method : 'post',
parameters : 'name=Quentin&country=Australia'
};
new Ajax.Request(url, options);
// method 2: create parameters as an object
var url     = '/url/to/request';
var options = {
method : 'post',
parameters : {
name    : 'Quentin',
country : 'Australia'
}
};
new Ajax.Request(url, options);

Event Callbacks

The next part of specifying the request options is to specify the functions that should be called when certain events occur. These are included in the options array just as above, except each value should be either a function or the name of a function to call. In the next section I'll show you how to create these functions.

The following list shows the most common callbacks that you will use. There are others available, but not all are available on all browsers.

  • onSuccess: This callback is triggered when a request is completed and the HTTP response code is in the 200s. In the past I have seen a lot of code utilizing XmlHttpRequest that checks specifically for a response code of 200, whereas this isn't necessarily the response code that will be returned. With onSuccess, this is not a problem.
  • onFailure: This callback is triggered when a request completes but the HTTP response code is not in the 200s. For instance, if a 404 File Not Found error occurs then this callback would be used.
  • onComplete: Regardless of whether a request is deemed to have succeeded or failed, this callback is triggered upon completion of a request. This is the final callback to be triggered. In other words, if you specify onSuccess or onFailure, then onComplete will be used after that.

Listing 6 shows an example of specifying these callbacks in the request options. Each callback accepts the response object (an instance of Ajax.Response) as the first argument. I will cover how to specifically use this response in the next section.

Listing 6 Specifying callbacks for success, failure and request completion (listing-6.js)
function onRequestSuccess(transport)
{
// handle the response
}
function onRequestFailure(transport)
{
// handle the response
}
function onRequestComplete(transport)
{
// handle the response
}
var url     = '/url/to/request';
var options = {
method : 'post',
parameters : {
name    : 'Quentin',
country : 'Australia'
},
onSuccess  : onRequestSuccess,
onFailure  : onRequestFailure,
onComplete : onRequestComplete
};
new Ajax.Request(url, options);

As an alternative to using onSuccess and onFailure, you can handle specific HTTP response codes. For instance, if you wanted a different handler for a 404 error to a 403 error, you could achieve this easily by specifying the on404 callback and on403 callbacks. You can use any HTTP code, in the format of onXYZ (where XYZ is the response code).

Note however that if you do this, the onFailure or onSuccess callback will not be used for that response code. As an alternative, you may wish to manually check for the response code and act accordingly from within either the onFailure or onSuccess callback.

Listings 7 and 8 show example of two different ways of handling response codes. In the first example, I have specified the on403 and on404 callbacks, while in the second I check the transport.status value to determine the code.

Listing 7 Specifying a separate callback for different status codes (listing-7.js)
var url     = '/url/to/request';
var options = {
/* other parameters if required */
on403 : function(transport) { ... },
on404 : function(transport) { ... }
};
new Ajax.Request(url, options);
Listing 8 Using a single callback and checking the status code (listing-8.js)
function onRequestFailure(transport)
{
switch (transport.status) {
case 403:
// 403 specific handler
break;
case 404:
// 404 specific handler
}
}
var url     = '/url/to/request';
var options = {
/* other parameters if required */
onFailure : onRequestFailure
};

Handling the Ajax Response

Now that you know how to specify the callbacks for particular events that occur when performing an Ajax request, I will show you write to write the callback handler.

All of these callbacks are passed an instance of Ajax.Response as the first argument. This object contains information about the request, such as the HTTP status code that resulted from the request, and any response data sent back.

My own preference is to call this parameter transport. Whatever you call it, be consistent. Functions should be self-documenting, so you should be easily able to determine what the function is used for by its name and the naming of its arguments.

In writing a handler we are typically concerned with the response data. Typically we want to use this data somehow on the current page, whether it's text, HTML, JSON or XML. Because of this, when writing a response handler we are really only concerned with the onSuccess handler, since onFailure occurs only if something went wrong with the request.

Even if the Ajax request resulted in some error condition in your application, if the Ajax request succeeded you then need to handle your application error in the onSuccess handler.

Note: An Ajax request doesn't have to return any data. Some operations involve you simply sending data to the server for it to process, without requiring any response. You don't really need any success or failure handlers if you don't care about the response.

The most commonly used values that are available in the transport variable are as follows:

  • status: The HTTP response code. An example of reading this value is shown in listing 8.
  • statusText: The text that corresponds to the response code, as sent by the server. Since different servers may send a slightly different string for certain response codes, you shouldn't rely on this value.
  • responseText: This is any data that is sent back from the request as a string.
  • responseXml: If data was sent from the server in XML format (with the correct content type header of text/xml), this variable contains the response as a document object.
  • responseJSON: If the data was sent back from the server in JSON format, you can read the data from this array. In the next section I will show you how to do this. Note that the response from the server must use the application/json content type header.

You can read any of these properties from the first argument that is sent to the request handlers. In the previous section I called this variable transport.

Listing 9 shows identical functionality to the Ajax.Updater example in Listing 3, however, in this example we read the responseText variable and update the element accordingly. This example uses the same example HTML, shown in Listing 2.

Listing 9 Populating an element using the responseText property (listing-9.html)
<html>
<head>
<title>Populating an element using the responseText property</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div>
<input type="button" value="Click Me!" id="myButton" />
</div>
<script type="text/javascript">
function onAjaxSuccess(transport)
{
$('myButton').up().update(transport.responseText);
}
function onButtonClick(e)
{
var options = {
method    : 'get',
onSuccess : onAjaxSuccess
};
new Ajax.Request('listing-02.html', options);
}
$('myButton').observe('click', onButtonClick);
</script>
</body>
</html>
 
new Ajax.Request(url, options);

Typically you will only ever need to use the onSuccess and onFailure handlers to do something with an Ajax response (although if you're lazy you may not even bother with the onFailure). Typically you won't really need to use the onComplete or other callbacks.

In the next section I will show how to actually do something useful with the callback handlers.

Using JSON Data

I've mentioned JSON a few times in this article so far, but what exactly is it? Short for JavaScript Object Notation, it is a way to send complex data structures between the client and server.

Essentially, it is just JavaScript code. However, it is really only the code that would be used to create a new array or object in JavaScript. Therefore, when you read the JSON data you can bind it to a JavaScript variable and access the response just as you would from any other array or object in JavaScript.

For example, if you wanted to create an array of data in PHP, then make that data easily accessible in JavaScript (let's say you wanted to send this PHP array back in an Ajax response), then you would use JSON.

Listing 10 shows an example of some data you are representing in PHP that you want available in JavaScript. Listing 11 shows the JSON representation of this data.

Listing 10 Some sample data in PHP that we want to use in JavaScript (listing-10.php)
<?php
$person = array(
'name'    => 'Quentin',
'country' => 'Australia'
);
?>
Listing 11 The $person array represented in JSON (listing-11.js)
{
name    : 'Quentin',
country : 'Australia'
}

This data is in the same format as JavaScript uses, meaning you can easily use it in your JavaScript code. Listing 12 shows how you might access this data using the responseJSON variable in your onSuccess handler for Ajax.Request. This assumes that the $person array was the only data sent in the response. In this example I assigned the JSON data to a variable called json, purely so the code is easier to read when the data is accessed.

Listing 12 Reading the JSON response (listing-12.js)
function onSuccess(transport)
{
var json = transport.responseJSON;
alert(json.name + ' is from ' + json.country);
}
PHP provides the json_encode() function to convert a PHP variable into its equivalent JSON format.

Handling Ajax Requests on the Server Side Using PHP

Now that you have some understanding of how JSON data works, I'll give you a concrete example of performing an Ajax request which returns some JSON data. Additionally, I'll now show you how to handle Ajax responses in PHP. The principles here apply to other languages also, but the server-side language used is PHP.

When writing scripts in PHP to handle Ajax requests, you typically write your scripts as you would for normal requests. The key difference is that you will typically want to set the content type of the response data. By default, PHP uses a content type of text/html, so if you're just sending HTML data back (as we did in listings 3 and 9), you don't need to set the content type.

As I mentioned in the previous section, PHP provides a function called json_encode() which you can use to convert a PHP variable (such as an array) into JSON format. You can use header() to send the content type, then echo the output from json_encode().

Listing 13 Sending a PHP array as JSON data (listing-13.php)
<?php
$person = array(
'name'    => 'Quentin',
'country' => 'Australia'
);
header('Content-type: application/json');
echo json_encode($person);
?>

We can now build on the code from Listing 12 to actually request this data and display it accordingly. Listing 14 shows the complete example of reading JSON from the server. When you click the button that is displayed, the Ajax request will be initiated. The response will then be displayed. Note however that there is no error handling in this example and it assumes that specific data will be returned.

Listing 14 Reading JSON data from the server (listing-14.html)
<html>
<head>
<title>Reading JSON data from the server</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<div>
<input type="button" value="Click Me!" id="myButton" />
</div>
<script type="text/javascript">
function onSuccess(transport)
{
var json = transport.responseJSON;
alert(json.name + ' is from ' + json.country);
}
function onButtonClick(e)
{
var options = {
method    : 'get',
onSuccess : onSuccess
};
new Ajax.Request('listing-15.php', options);
}
$('myButton').observe('click', onButtonClick);
</script>
</body>
</html>

When handling an Ajax request on the server-side, you may want to ensure that the request did in fact come via Ajax. Whenever a request is performed using Prototype's Ajax.Request, the header X-Requested-With header is sent, with a value of XMLHttpRequest.

While this value can be manually set by a clever user, ultimately it doesn't matter too much and simply allows you perform different functionality if a random user happened to stumble across your Ajax request handler.

Listing 15 shows an example of checking for this header. This is a modification of Listing 13, in which we send a different content type based on how the request is performed. Try viewing this script in your browser. Realistically you may prefer to redirect to another page rather than what is done here.

Listing 15 Checking the request method for the current script (listing-15.php)
<?php
$person = array(
'name'    => 'Quentin',
'country' => 'Australia'
);
$isXmlHttpRequest = isset($_SERVER['HTTP_X_REQUESTED_WITH']) &&
strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest';
if ($isXmlHttpRequest)
header('Content-type: application/json');
else
header('Content-type: text/plain');
echo json_encode($person);
?>

In PHP, you read the header from $_SERVER. PHP modifies the request header names by prepending HTTP_, capitalizing the name and replacing hyphens with underscores. In other words, X-Requested-With becomes HTTP_X_REQUESTED_WITH. Additionally, I like to use strtolower() on this value so there is no confusion between, say, XMLHttpRequest and XmlHttpRequest.

Summary

In this article I showed you how to perform Ajax requests using the Ajax.Request provided by Prototype. This class is a wrapper to the XMLHttpRequest object that modern browsers have. I showed you how to specify options when performing a request, including how to specify the callback handlers to deal with the response from the HTTP sub-request.

Additionally, I introduced you to JSON data and how to send JSON data with PHP, and receive it in the Ajax response. While the examples given were mostly conceptual, hopefully they gave you a good idea of how you can use Ajax in your own applications. In the eighth article of this series a larger, more concrete example will be given when I bring together all of the Prototype functionality covered in this series.

In the next article I will show you how to create JavaScript classes in Prototype. If you prefer an object-oriented approach to your programming, you will find this article invaluable, since knowing how to effectively create classes will give your code a much better overall structure.

 

 

 

 

 

 

 

 

 

 
 
Add a Comment:
 
(You must be signed in to comment on an article. Not a member? Click here to register)
   
Title:

Comments: