 | Level: Intermediate Michael Galpin (mike.sr@gmail.com), Software architect, eBay
13 May 2008 If you're developing Web applications these days, then you're doing Ajax
development. Ajax is no longer something unusual that you add to your applications
in special cases. It has become an integral part of Web development. To some,
enhancing applications with Ajax used to be a tricky proposition. Cross-browser
limitations to deal with, writing a lot of complicated JavaScript, and learning
about magic numeric codes within that JavaScript were just a few of the challenges facing Ajax developers. Thankfully, several open source JavaScript libraries are available now to make things much easier. In this first article in a three-part series, you will create an Ajax application for managing songs using the Prototype JavaScript library and script.aculo.us.
This three-part article series looks at using two separate open source projects, the
Prototype JavaScript library and script.aculo.us, to create amazing Ajax applications
for your Web 2.0 Web site. In this article, Part 1 of the series, you will be working
the Prototype JavaScript library (see Resources for a link).
This article uses the latest release of Prototype (at the time of this writing) version 1.6.0.2. Ajax involves dynamic data, and so a server-side technology is needed. In this article, we use PHP 5.2.1 in combination with Apache 2.0.59 and MySQL 5.0.41 (see Resources). You can definitely pick your own programming language, Web server, and database.
 |
The developerWorks Ajax resource center
Check out the Ajax resource center,
your one-stop shop for free tools, code, and information on developing Ajax applications. The active Ajax community forum, hosted by Ajax expert Jack Herrington, connects you with peers who might just have the answers you're looking for right now.
|
|
Introduction to Prototype
Do a search and you'll find a lot of JavaScript libraries out there. There are two main reasons for this. First, JavaScript is the language of the browser and thus has become a key part of software development. Everyone is writing JavaScript code, and hence a lot of JavaScript libraries exist. Second, JavaScript is very tricky. The quirks among different browsers often make JavaScript development somewhat painful. Fortunately, JavaScript libraries often provide abstractions to ease this pain. Prototype is such a library.
Prototype is a fairly broad library, with lots of functionality. Its features help make
common tasks easier, with an emphasis on Ajax. Prototype provides very cool ways to
implement Java™- and C++ style inheritance in JavaScript, extensions to the HTML DOM elements, and utilities for working with JSON. In this article, you will concentrate on what Prototype does for Ajax, and you'll learn about a few of Prototype's other features along the way.
Using Prototype's Ajax library
Prototype has numerous features designed to help you with Ajax development. One reason for Prototype's popularity is that it does not restrict how you do Ajax programming. For example, there are two common patterns for responding to an XMLHttpRequest (the underlying mechanism in Ajax). One is to respond with HTML that is used to repaint part of the screen. The other is to respond with data and leave it up to some other JavaScript code to parse the data and repaint. Prototype supports both of these patterns. Let's look at how it enables the first technique, responding with HTML.
Working with HTML
Prototype makes it very easy to work with server-produced HTML on an Ajax response. It provides an API built just for this and allows your server-side code to simply produce an HTML fragment, with no special, Prototype-specific format. The easiest way to learn about this is to simply watch it in action.
In our example, we are building an application for managing songs, which needs a
database. Listing 1 shows a script for creating the database:
Listing 1. Create database script
CREATE TABLE 'songs' (
'id' int(11) NOT NULL auto_increment,
'title' varchar(120) NOT NULL,
'artist' varchar(120) NOT NULL,
'genre' varchar(80) default NULL,
'album' varchar(120) default NULL,
'year' year(4) default '2008',
PRIMARY KEY ('id')
) ENGINE=MyISAM AUTO_INCREMENT=5 DEFAULT CHARSET=latin1
|
As mentioned earlier, I am using MySQL, but, you can easily adapt this script for another database. You will want to create some sample data using the supplied script (see the Download section).
Now let's create a page for viewing our music library, which will be a pure HTML page,
as shown in Listing 2:
Listing 2. The index.html page
<html>
<head>
<title>MyTunes Library</title>
<script type="text/javascript" src="prototype.js"></script>
<script type="text/javascript">
function loadTunes(){
new Ajax.Updater('tunesBox', 'list.php', { method: 'get' });
}
</script>
<link rel="stylesheet" href="tunes.css" type="text/css"/>
</head>
<body onload="loadTunes();">
<div id="pageTitle">MyTunes</div>
<div id="tunesBox">
<img id="spinner" src="wait_spinner.gif" height="33" width="33"/>
</div>
</body>
</html>
|
Now is where things get interesting. The page shown in Listing 2 is static, pure HTML and JavaScript code. This means it can be cached by browsers and relieve load on your servers. When the page loads, the JavaScript function loadTunes() is invoked. This is where you use Prototype, and in particular its Ajax.Updater object. Prototype handles the task of creating the browser-appropriate transport mechanism for sending Ajax requests. While this task was once quite a big deal, now we almost take it for granted because of the pervasiveness of libraries like Prototype.
The object (Listing 2) takes an ID of an element on the page, a URL of a Web server,
and a map of options. In this case, we give it an ID of tunesBox. You can see there is a div on our page with this ID. The URL we have given it is "list.php. Prototype is going to invoke this URL, take its response, and dump it into the tunesBox div. Finally, the only option we have specified is to use an HTTP GET instead of the (default) POST. A POST would be OK, too, but we have followed the REST standard here and used a GET because we are only reading the data. Let's take a look at the PHP file that we have told Prototype to invoke (see Listing 3):
Listing 3. The list.php file
<?php
$message = "";
$sql = "select * from songs";
try{
require_once(dirname(__FILE__)."/db.php");
$result = mysql_query($sql,$conn);
$list = array();
if (!$result) {
$message = "Could not successfully run query ($sql) from DB: " . mysql_error();
} else if (mysql_num_rows($result) == 0) {
$message = "Your library is empty!";
} else {
while ($row = mysql_fetch_assoc($result)){
array_push($list, $row);
}
}
mysql_free_result($result);
} catch (Exception $e) {
$message = "Sorry there was an error: " . mysql_error();
}
?>
<?php if ($message): ?>
<div><span class="error"><?= $message ?></span></div>
<?php else: ?>
<table border="1" width="100%" cellpadding="8">
<thead>
<tr>
<td>Name</td>
<td>Artist</td>
<td>Album</td>
<td>Genre</td>
<td>Year</td>
</tr>
</thead>
<tbody>
<?php foreach($list as $song): ?>
<tr>
<td><a href="edit.html?id=<?= $song["id"] ?>"><?=
$song["title"] ?></a></td>
<td><?= $song["artist"] ?></td>
<td><?= $song["album"] ?></td>
<td><?= $song["genre"] ?></td>
<td><?= $song["year"] ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
|
This is a pretty straightforward PHP script. It queries the table created earlier, and
then iterates over the result set, creating an HTML table. Nothing really special going
on here. Deploy everything (including the JavaScript, CSS, and other PHP files that you can find in the download code) to your PHP server and bring it up in a browser, as shown in Figure 1:
Figure 1. MyTunes
The page should load and seamlessly use Ajax and Prototype to get the table of songs
from the server. To take a look at what is going on in more detail, you can use a tool
like the popular Firefox extension Firebug (see Resources for a
link). Figure 2 shows its output:
Figure 2. Firebug's Console for MyTunes
If you click its Net>XHR tab, you will see that Prototype created a XMLHttpRequest for you. It also created the JavaScript handler function for the response. That is what actually used the response from the PHP script to dynamically alter the HTML DOM. From Listing 2, you can see that all of this was accomplished with just one line of code. Ajax doesn't get much easier than this. As mentioned earlier, this is one common pattern with Ajax. Another is to only return data, with no markup (HTML) from the server. Let's take a look at how Prototype helps with that approach as well.
Working with data
It can be very convenient to have your server return an HTML for Ajax request, but it
does have its disadvantages. Because both markup and data are being returned from the server, this wastes some bandwidth. It can also become very complex when the HTML needs to have JavaScript (or, at least event listeners) attached to it, causing you to keep track of client state on the server. Sometimes it is easier to just return pure data from the server. Once again, Prototype makes this easy to do too. Let's once again use an example to illustrate this.
You might have noticed in the previous example that the name of each song was actually
a link to another page, edit.html, as shown in Listing 4:
Listing 4. The edit.html page
<html>
<head>
<title>Edit a Song</title>
<script type="text/javascript" src="prototype.js"></script>
<script type="text/javascript" src="editor.js"></script>
<link rel="stylesheet" href="tunes.css"/>
</head>
<body onload="loadSong();">
<div id="pageTitle">Edit Song</div>
<div id="tunesBox">
<span id="spinner">
<img src="wait_spinner.gif" height="33" width="33"/>
</span>
<form id="songForm" onsubmit="catchSubmit();">
<input type="hidden" name="id" id="id"/>
<div id="name">
<span id="nameLbl">Name:</span>
<span id="title" onclick="edit(this)"?></span>
</div>
<div id="artistDiv">
<span id="artistLbl">Artist:</span>
<span id="artist" onclick="edit(this)"?></span>
</div>
<div id="albumDiv">
<span id="albumLbl">Album:</span>
<span id="album" onclick="edit(this)"?></span>
</div>
<div id="genreDiv">
<span id="genreLbl">Genre:</span>
<span id="genre" onclick="edit(this)"?></span>
</div>
<div id="yearDiv">
<span id="yearLbl">Year:</span>
<span id="year" onclick="edit(this)"?></span>
</div>
</form>
</div>
<div class="backLink">
<a href="index.html">Back to MyTunes Library</a>
</div>
</body>
</html>
|
Once again, this is a static page, all HTML. This time when the page loads, it invokes
a JavaScript function called loadSong(). That function is in a separate file, editor.js. Let's take a look at the loadSong function from this file (see Listing 5):
Listing 5. The loadSong() JavaScript function
function loadSong(){
var params = window.location.search.parseQuery();
function loadSong(){
var params = parseQueryString();
var id = params["id"];
// create handler that will be invoked
// when response is received from server
var handler = function(xhr){
// use responseJSON property added by Prototype
var json = xhr.responseJSON;
// check for error
if (json.error){
// display the error
}
var song = json.song;
// clear the spinner
// use Prototype's $() shortcut notation
$("spinner").innerHTML = "";
// set the display data
$("id").value = song.id;
$("title").innerHTML = song.title;
$("artist").innerHTML = song.artist;
$("album").innerHTML = song.album;
$("genre").innerHTML = song.genre;
$("year").innerHTML = song.year;
};
// create options for
var options = {
method : "get",
onSuccess : handler,
parameters : { "id" : id }
};
// send the request
new Ajax.Request("song.php", options);
}
|
The expression window.location.search gives you access to
the page's URL query string. In this case, it will be something like "?id=2". Then one
of the parseQuery() functions is used. This is not some
special function that is inherent in the window.location.search object — that object is just a string. The parseQuery() function is a function that Prototype adds to JavaScript's string class, so it can be called on any string. The parseQueryString() function does just what it says. It turns the query string of the URL of the page into a map of name/value pairs. This lets you get the ID parameter that was passed in from the previous page. You then call the PHP script song.php with this parameter. This is the server side script that loads the song and returns it to you. Its code is shown in Listing 6:
Listing 6. The song.php script
<?php
header('Content-Type: application/json');
$message = "";
$resp = array();
$sql = "select * from songs where id =" . $_REQUEST["id"];
try{
require_once(dirname(__FILE__)."/db.php");
$result = mysql_query($sql,$conn);
$song = array();
if (!$result) {
$message = "Could not successfully run query ($sql) from DB: " .
mysql_error();
} else if (mysql_num_rows($result) == 0) {
$message = "No song found with that id!";
} else {
$song = mysql_fetch_assoc($result);
}
$resp["song"] = $song;
mysql_free_result($result);
} catch (Exception $e) {
$message = "Sorry there was an error: " . mysql_error();
}
$resp["error"] = $message;
echo(json_encode($resp));
?>
|
Listing 6 queries the database. Notice that you are sending back JSON code. You could
have used XML for the data transfer, but JSON is slightly more efficient and easier to
work with on the client. To correctly send JSON back, you set the content-type as
application/json. This is not just correct with respect to the browser, but it is also a requirement of Prototype. While it is not a strict requirement for Prototype, it lets Prototype identify the response as JSON and make your life a little easier. Finally, to return the data as JSON, you use PHP's built-in json_encode function. (There will be similar library functions in whatever language you are using.)
Now let's go back to Listing 5 and look at the response handler
(var handler = function ...) that was created. Prototype automatically does a safe-eval of the JSON returned; it provides it via the responseJSON property on the object it passes to the handler. This lets you easily access the data, such as a song.title, and so on. Now take a look at the line that starts with $("spinner"). The $() notation is a shortcut that Prototype provides. It is essentially a shortcut for the familiar document.getElementById() function. So $("spinner") is basically equivalent to document.getElementById("spinner"). Prototype actually adds some extra methods to the return object, depending on its type, as you will see later. With Prototype's help, you are able to initialize your display using simple syntax such as $("title").innerHTML = song.title. In the browser, this should look like Figure 3:
Figure 3. The Edit page: After initialization
Once again, you can use Firebug to watch the data being fetched form the server, as
shown in Figure 4:
Figure 4. Firebug showing song data being loaded
Now the title says "Edit Song," but this page looks to be read-only. Just click on one
of the attribute values and you can edit it, as shown in Figure 5:
Figure 5. Editing a song
If you tab-out, or press Enter, the data is sent to the server and updated in
the database. The code for sending it to the server is again from the editor.js file and is shown in Listing 7:
Listing 7. JavaScript for updating song
function makeText(input){
// save record
var formData = $("songForm").serialize(true);
saveData(formData);
// go back to display
input.parentNode.innerHTML = input.value;
}
function saveData(song){
var handler = function(xhr){
var json = xhr.responseJSON;
if (json.error){
// display the error
}
};
var options = {
method : "post",
onSuccess : handler,
parameters : song
};
new Ajax.Request("update.php", options);
}
|
The code starts in the makeText() function. Now when you use
the Prototype shortcut $("songForm"), you get its enhanced
Form object. This gives a new function to call, the serialize function, which gets all the input fields in the form and creates a hash of their names and values. You pass that hash to the saveData() function. Once again, Prototype is used to send an Ajax request to the server. Notice that this time a POST is used because data is being changed and update.php is being called (see Listing 8):
Listing 8. The update.php script
<?php
header('Content-Type: application/json');
$message = "";
$clause = "";
foreach ($_POST as $key => $value){
if ($key != "id"){
$clause = $key . " = '" . $value . "' ";
}
}
$sql = "update songs set " . $clause . " where id=" . $_POST["id"];
try{
require_once(dirname(__FILE__)."/db.php");
$result = mysql_query($sql,$conn);
if (!$result) {
$message = "Could not successfully run query ($sql) from DB: " .
mysql_error();
}
mysql_free_result($result);
} catch (Exception $e) {
$message = "Sorry there was an error: " . mysql_error();
}
$resp["error"] = $message;
echo(json_encode($resp));
?>
|
Once again, you see an example of fairly simple PHP processing. Notice that the Ajax
request only sent back the ID of the song and the field being modified, an example of Ajax efficiency with database modification. You can see all of this in Firebug once again as shown in Figure 6:
Figure 6. Firebug showing song update
Prototype has provided convenient methods for not only sending an Ajax request, but also for accessing the data being sent, accessing the response data, and modifying the display.
Summary
The world of Ajax is not quite the unknown frontier it was just a few years ago. The smart developer can make the most of freely available open source libraries to reduce the accidental complexity of some Ajax programming. Prototype is a powerful library with that simplification goal as its hallmark. In addition to simplifying all aspects of Ajax development, it works equally well with content-driven (HTML returned from server) and data-driven approaches to Ajax. It's a must-have tool in any experienced Web developer's toolbox.
Download | Description | Name | Size | Download method |
|---|
| Part 1 sample code | wa-aj-ajaxpro1.zip | 39KB | HTTP |
|---|
Resources Learn
-
See how you can use Prototype to create an Ajax style chat program in the developerWorks
article "
Ajax and XML:
Ajax for chat" (Jack Herrington, developerWorks, December 2007).
-
script.aculo.us
provides you with
easy-to-use, cross-browser user
interface JavaScript libraries.
-
Prototype is a key component in the Ajax support in Ruby on Rails. Read about it in the
developerWorks article "
Crossing borders: Ajax on Rails" (Bruce Tate, developerWorks, December 2006).
-
Get a survey of popular Ajax libraries, including Prototype, in the developerWorks article
"
Ajax -- a guide for
the perplexed: Survey of Ajax tools and techniques" (Gal Shachor et al., developerWorks, July 2007).
-
Rico is another popular library that is actually built on top of Prototype. Read about it
in the developerWorks article "Create data set navigation with Rico" (Nikhil Parekh, developerWorks, January 2007).
-
Find out more about some of the Ajax patterns mentioned here in the developerWorks article
"
Ajax and XML:
Five common Ajax patterns" (Jack Herrington, developerWorks, March 2007).
-
"Discover the Ajax
Toolkit Framework for Eclipse" (Tim McIntire, developerWorks, November 2006): Extend the Eclipse Web Tools Platform when you add support for open-source Ajax tool kits such as Dojo, Zimbra, and Rico.
-
"
Ajax for Java
developers: Build dynamic Java applications" (Philip McCarthy, developerWorks, September 2005): Explore how Ajax out-navigates the traditional Web paging model.
-
See more articles and tutorials on the Prototype Javascript library.
-
The server scripts used in this article conform to the REST protocol. Learn more about
using REST and Ajax together in the developerWorks article "RESTful Web services and their Ajax-based clients" (Shailesh K. Mishra, developerWorks, July 2007).
-
Read Simon Willison's A (Re)-Introduction to JavaScript.
-
Check out the developerWorks Ajax Resource Center, which is packed with resources to build your Ajax skills.
-
W3Schools provides online reference information for all core Ajax technologies (JavaScript, CSS, HTML, DOM, XML, and so on).
-
To listen to interesting interviews and discussions for software developers, check out developerWorks podcasts.
-
developerWorks technical events and webcasts: Stay current with developerWorks technical events and webcasts.
Get products and technologies
-
Prototype is a JavaScript library that introduces powerful functions to help simplify Ajax programming.
-
Get the Firebug extension for Firefox.
-
Get PHP.
-
Get Apache.
-
Get MySQL.
About the author  | |  | Michael Galpin has been developing Web applications since the late 1990s. He holds a degree in mathematics from the California Institute of Technology and is an architect at eBay in San Jose, CA.
|
Rate this page
|  |