Download the
Ajax.zip archive.
Install this as a Java Web Application with Existing Sources.
Add the CommonsLang Library as used in
java-forms.html.
This application contains the important jQuery JavaScript package which
is represented by the single file
in the js folder of the Web Pages
(the actual folder is web).
jquery.js
JavaScript, Ajax, Toolkits
AJAX is an acronym for
Asynchronous Javascript And Xml.
The most important point that AJAX is about using
JavaScript to make asynchronous
server-side calls without
having to do a browser reload, allowing web applications
to behave more like standard
graphical user interface applications.
Modern web applications often feature specialized effects
accomplished by JavaScript code written using client-side toolkits.
Writing in JavaScript directly tends to be quite tedious and
error-prone. The JavaScript language, per se, lacks capabilities
for modularity, sharing and testing. Errors are often difficult
to track down and browser differences become prominent.
The point about client-side toolkits is that they:
JavaScript used in any setting is a difficult language to debug.
Fortunately there are some tools and techniques that make it possible.
Here are some key points:
Use Firefox (sorry IE devotees)!
Firefox's built-in JavaScript debugger is the Error Console
(accessed through the
Tools Error Console menu)
which often suffices to debug JavaScript.
Install
Firebug.
Once installed, access it by:
Tools Firebug Open Firebug.
The HTML tab will display JavaScript-generated code which
is not visible by viewing the source code.
The console tab will view show information about
the AJAX activations which is not visible directly in
any other way.
JavaScript uses a form
of lazy object construction in that its
objects are not declared anywhere, just created on the fly.
This means
that you can add new members at will
to almost any existing object.
The big downside of this approach is that most misspellings
of a subobject will not be regarded as errors.
For example, if you mean to write:
there will be no error, but "nothing will happen," making
it hard to track down.
This points to one of the advantages of client-side
toolkits in that they provide alternative operations
which avoid direct usage of JavaScript coding.
Alerts and console messages. A common way of debugging
JavaScript is to issue alert("...") statements which
invoke a popup message. More subtle is the usage of
console.log("...") messages which are issued to
the Firebug console.
It is often useful to test the server-side handlers "by hand", independently
of their being called by a JavaScript activation.
In this case, you'll want to manipulate the browser's
location line by hand, passing the parameters
via the
query string. For example, assuming that
xx and yy are the relevant incoming query parameters, try:
The JavaScript language shares much of the same syntax as the Java
language minus the strong typing. Strings can be delimited by double or
single quotes and there is only one numeric type (represented by
a double).
Other than language details, the most significant difference with Java
is that JavaScript is built in to the standard browsers
in order program all the browser features according to the
so-called Document Object Model (DOM).
The DOM supports at its top level the window object, representing
the entire browser. The subobjects of window are these:
document (referring to the HTML document
contents),
navigator (information about the browser type, version, etc),
location (information about the URL being accessed),
history (access to previous pages),
status (the status bar at bottom of browser),
frames[] (access to frames in a frame-based layout).
For example, the expression window.document refers
to the document part of window object.
In all cases, JavaScript
permits the omission of the top-level "window." syntax
and simply write "document" (and others).
Introducing JavaScript code into HTML
JavaScript code is typically
made available through the script tags:
<script type="text/javascript">
<!--
JavaScript code here
// -->
</script>
These lines make a browser which does not recognize JavaScript
ignore the code between:
<!--
and
-->
Alternatively, an external code file, say
extern-code.js
can be loaded with this script tag usage:
Our JavaScript code of interest consists mostly of functions called by
JavaScript events. For example, we can create a "generic" button which
calls a JavaScript function as follows:
function my_button_handler()
{
// what to do when the button is pressed
}
Reading/Writing HTML elements from JavaScript
Within JavaScript code we need to be able to read and write data from
the HTML elements.
JavaScript provides a number of ways to obtain a DOM object
for an HTML element,
but perhaps the simplest and direct way
is by assigning an id attribute
to the element and obtaining an object for it with the function
document.getElementById.
For example, if we define:
<input type="text" id="tf" />
then, within JavaScript we can obtain an
object representing this textfield
with the statement:
var tf = document.getElementById( "tf" )
JSON format
JSON (JavaScript Object Notation)
is a format for expressing structured object literals.
This notation is used heavily in most of the client-side toolkits as
parameters of the function calls.
For example:
{ x: 222, y: "hello" } -- an object with members x and y
[ 12, "hello", 15 ] -- an array
{ z: [ 22, 33 ] } -- an object with array value
[ {x: 12, y: "aa"}, {x: 17, y: "bb"} ] -- an array of objects
Not only are these JSON literals used to call client-side functions, they
can be used to transmit structured data back to JavaScript after a server-side
call. What this means is that the server creates a JSON literal, the JavaScript
function retrieves it and then converts it into a JavaScript object by
the eval function:
var data = eval( "(" + JSON-literal + ")" );
After that, we can access data
as a JavaScript object like this
(respectively):
data.x, data.y -- an object with members x and y
data[0], data[1], ... -- an array
data.z[0], data.z[1] -- an object with array value
data[0].x, data[1].y -- an array of objects
Both Java and JavaScript support a "comma at the end" of the last
array element, e.g.
[ 12, "hello", 15 ] is the same as [ 12, "hello", 15, ]
However, Internet Explorer, at least is some versions, appears
to put an extra null at the end of array in the latter case.
So it's better to avoid the "comma at the end."
XML format is an alternative format for transmitting structured data from
a server-side call back to the client. However, the extraction of
the structured information from the XML literal is significantly slower
than a JSON literal, and JSON is generally preferred over
XML for reasons both of efficiency and simplicity.
In retrospect "AJAX" probably should have been "AJAJ";
however the former definitely sounds cooler.
jQuery Preliminaries
The jQuery package has the advantage of being usable without having to
modify the HTML, thus most jQuery JavaScript code can be introduced
through external script files. The only requirement of the HTML code is that
key elements specify certain attributes, of which, the most
basic being the id attribute (which would be the case for
any JavaScript usage).
In particular, we can avoid the insertion of JavaScript code in the
handler attributes (onclick, onsubmit, onchange, etc). The definitions
of the handler attributes can be effected all through jQuery code outside
the document's body.
According to the jQuery usage logic, all the event-handling definitions
are enclosed within the structure:
$(document).ready(
function() {
...
}
)
meaning, when the document is ready, call the function to effect certain
actions. jQuery and other toolkits make heavy usage of these "anonymous"
functions which do something when an event takes place.
The "$" is actually the special
jQuery object from which everything else is based. This seems odd, but
$ is actually a legal identifier character in JavaScript, and thus
$ by itself is a legal identifier, along with others like
$x, x$, $x$y, etc.
The next jQuery notational convenience is that the above can expressed
and rewritten as this:
$( function() {
...
})
It may look a bit too compact, but the crunched syntax "})"
actually works well with NetBeans formatter.
Identifying HTML elements
An element's id attribute is commonly used in JavaScript
in conjuction with the expression:
var elt = document.getElementById("elt_id")
The jQuery equivalent is the expression:
$("#elt_id")
which retrieves a certain jQuery object from which we can express operations.
If you know CSS notation, the "#" is actually a perfect choice for
the designator prefix. Although we do not need more than this, jQuery also
makes it easy to effect operations on a set of elements. Specifically the
expression
$(".some_class")
is an object from which we can effect changes to all elements which define:
class="some_class"
Handling an onclick event
A very common situation is to activate some JavaScript code, like an
AJAX call when the user clicks a button or hyperlink. The way jQuery expresses
this is by the code:
The JavaScript onclick event is turned into the jQuery click
function. As with "onload", the argument passed to the click function
is a function which will be executed when the onclick event happens.
In the case of hyperlink clicking, jQuery usually wants to "take control"
of the actions and so must prevent the hyperlink's default behavior. The
is done very cleanly by realizing the event argument passed to the click
function as follows:
package models;
public class People {
private String people[]
= new String[] { "Bob", "Bill", "Jan", "Ellen", "Ken", };
public int getNumPeople() { return people.length; }
public String getPerson(int num) { return people[num]; }
}
Using this model, the mapping of the person number to name
is effected entirely through the client (i.e. all information is
available at the browser level):
$(function() {
$("#button").click(function() {
$("#out").html( $("#sel").val() )
})
})
The jQuery usage brings up two new functions:
$("...").html(...) // corresponds to ".innerHTML"
$("...").val(...) // corresponds to ".value"
The jQuery prescription is as follows:
when either of these functions are used without a parameter, the current
content is returned;
when used with a parameter, the content
is set by the argument.
The "val()" function applies to text/password
fields, textareas and selection lists. The "html()" function
is used for most other elements: paragraph, header, table cells, span, div, etc.
First AJAX usage
This version appears the same on the surface, but is entirely different
because only the person numbers are available to the browser and the
association of persons is done through a server-side AJAX call.
The commented section indicates an alternative method for invoking the ajax GET
call via the higher level $.get function. As can be expected,
there is a $.get function which corresponds to using POST type in
the ajax call.
Observe the simplicity of the servlet which obtains the data. It "returns"
the information by simply printing it.
AJAX HTML data transmission
We start with a relatively simple model for books:
models/Book.java
package models;
public class Book {
private String title;
private String type;
private int qty;
private String comments;
public String getTitle() { return title; }
public String getType() { return type; }
public int getQty() { return qty; }
public String getComments() { return comments; }
public Book(String title, String type, int qty, String comments) {
this.title = title;
this.type = type;
this.qty = qty;
this.comments = comments;
}
}
models/Books.java
package models;
import java.util.*;
public class Books {
Map<Integer,Book> books = new LinkedHashMap<Integer,Book>();
public Books() {
books.put(11, new Book(
"Java Swing", "paper", 3, "This book is all fine."));
books.put(27, new Book(
"\"Fun\" with <HTML>", "cloth", 5,
"Trickier than quotes" + "\n" +
"are new lines." + "\n\n" +
"Creating separate paragraphs" + "\n" +
"is another issue")
);
}
public Book fetch(int id) { return books.get(id); }
public Set<Integer> fetchIds() { return books.keySet(); }
}
Unlike the above notion of "person", a book consists of structured
data. This first version transmits the books "printout" using a
JSP file as the server-side handler:
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@ page import="models.*" %>
<%@ page import="util.Escape" %>
<jsp:useBean id="books" scope="session" class="models.Books"/>
<%
String id = request.getParameter("id");
int num_id = Integer.parseInt(id);
Book book = books.fetch(num_id);
int qty = book.getQty();
String comments = book.getComments();
String title = book.getTitle();
String type = book.getType();
title = Escape.html(title);
comments = Escape.html(comments);
// decide how to deal with linebreaks: none or one of these two
//comments = Escape.lineBreakToBr(comments);
//content = Escape.paragraphBreakToBrs(content);
%>
<table border="1" cellpadding="5">
<tr>
<td>title:</td> <td><%=title%></td>
</tr>
<tr>
<td>type:</td> <td><%=type%></td>
</tr>
<tr>
<td>qty:</td> <td><%=qty%></td>
</tr>
<tr>
<td>comments:</td> <td><%=comments%></td>
</tr>
</table>
The novelty is the usage of the JSP script,
handler/book_table.jsp
for the server-side call. We place it in the dedicated handler
folder to conceptually separate it from the other scripts.
Formatting operations
The other important idea is the usage of
various "escaping" operations to appropriately format the text for
display in HTML.
A key class which we will use in many circumstances is the one
which does various text formatting operations.
util/Escape.java
package util;
import org.apache.commons.lang.*;
public class Escape {
public static String html(String input) {
return StringEscapeUtils.escapeHtml(input);
}
public static String javaScript(String input) {
return StringEscapeUtils.escapeJavaScript(input);
}
public static String lineBreakToBr(String input) {
return input.replaceAll("\\n", "<br />");
}
public static String paragraphBreakToBrs(String input) {
return input.replaceAll("\\n\\s*\\n", "<br /><br />");
}
}
The experiments to try are indicated in the comments in
handler/book_table.jsp. Uncomment one of the two (not both)
alternatives:
// decide how to deal with linebreaks: none or one of these two
//comments = Escape.lineBreakToBr(comments);
//content = Escape.paragraphBreakToBrs(content);
AJAX JSON data transmission
A better way, in the sense of speed, to achieve the same effect is
to transmit the desired book data more-or-less as a book, not an HTML
table. The transmission will be as a JSON object which can be employed
to create a facsimile of a book. The downside of this approach is the
complexity of proper handling of the book's data values so that it can
be converted to a JavaScript object.
$(function() {
$("#button").click(function(evt) {
evt.preventDefault() // make sure the hyperlink doesn't work per se
$.ajax( {
type: "GET",
url: "GetBook",
data: { id: $("#sel").val() },
dataType: "json",
success: function(data) {
$("#out").css("visibility","visible")
$("#title").html(data.title)
$("#type").html(data.type)
$("#qty").html(data.qty)
$("#comments").html(data.comments)
}
} )
})
})
servlet/GetBook.java
package servlet;
import java.io.*;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import models.*;
import util.Escape;
public class GetBook extends HttpServlet {
protected void processRequest(
HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
try {
HttpSession session = request.getSession();
Books books = (Books) session.getAttribute("books");
String id = request.getParameter("id");
int num_id = Integer.parseInt(id);
Book book = books.fetch(num_id);
String title = book.getTitle();
String type = book.getType();
int qty = book.getQty();
String comments = book.getComments();
// filter for title, assume no newlines
title = Escape.html(title);
// filter for comments
comments = Escape.html(comments);
// decide how to deal with linebreaks: none or one of these two
//comments = Escape.lineBreakToBr(comments);
//comments = Escape.paragraphBreakToBrs(comments);
//finally, escape for JavaScript when using JSON:
title = Escape.javaScript(title);
comments = Escape.javaScript(comments);
out.println(
"{"
+ " title:" + '"' + title + '"' + ","
+ " type:" + '"' + type + '"' + ","
+ " qty:" + qty + ","
+ " comments:" + '"' + comments + '"'
+ " }"
);
} finally {
out.close();
}
}
// ...
}
Here is how we compare this script to the previous example:
The first indication of the difference with the previous example is
the usage of option:
dataType: "json"
in the ajax call. The $.get call will have the same effect if given yet
one last parameter, "json", specifying the dataType.
The second difference is that the handler now sends back the book in JSON
format. Here is where the complexity comes: formatting the text so that
it is acceptable when JavaScript converts it into an object. The key
commons/lang function:
StringEscapeUtils.escapeJavaScript(input);
is employed to achieve this effect, in this case through a function in
the util.Escape class.
The third difference is how the data is treated in the success operation.
In this case, the data, being an object, is split up into its
components and distributed into the output.
The fourth difference is that the table is now part of the initial script,
not generated from the server-side call. The places to put the four
data components in the table have been given ids, so that the JavaScript
handler can identify where to put the corresponding values.
AJAX Form Handling
This last example employs the jQuery plugin called jquery.form.js. It takes
a form, which could be used directly for a server-side non-AJAX call and
makes an AJAX call. The function used is $.ajaxForm, which employs all
the same options as the $.ajax call, but, by default, will take the
information out of the form tag itself. We only need to provide an
id for the form.
The operation of the script is simply to "echo" the data within the form
to the output below. Because the data is structured, we use JSON to transmit
it back.
The other important point about this is that the POST method is more-or-less
required due to the large amount of data being transmitted.
form_submit.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<%@ page import="java.util.*" %>
<%@ page import="models.*" %>
<jsp:useBean id="books" scope="session" class="models.Books"/>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>AJAX Form Submission</title>
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/errors.js"></script>
<script type="text/javascript" src="js/jquery.form.js"></script>
<script type="text/javascript" src="js/form_submit.js"></script>
<style type="text/css">
.hdr { font-weight: bold; color: red; }
</style>
</head>
<body>
<h3>AJAX Form Submission</h3>
<form id="inform" action="ProcessData" method="POST">
<input type="hidden" name="id" value="3" />
<table width="100%">
<tr>
<td width="80px">subject:</td>
<td>
<input type="text" name="subject" value="Session Management" />
</td>
</tr>
<tr>
<td width="80px">content:</td>
<td>
<textarea name="content" rows="10" cols="" style="width:100%;">
Session management is a mechanism for maintaining and sharing
the state of variables (called session variables) for every
participating program in a web project.
These variables are not lost when you go offsite and return.
This is one of the key ingredients of a web-based shopping cart
used in e-commerce.
The server can maintain a relationship with a browser session by
sending and receiving information packets called cookies.
A cookie is simply a name/value pair of data which contains
other attributes such as the target URL (server name and path)
and expiration timestamp.
</textarea>
</td>
</tr>
<tr>
<td></td>
<td><button id="b">Process</button></td>
</tr>
</table>
</form>
<hr />
<span class="hdr">id</span>: <span id="out_id"></span>
<br />
<span class="hdr">subject</span>:
<div id="out_subject"></div>
<span class="hdr">content</span>:
<div id="out_content"></div>
</body>
</html>