• No results found

Dynamically Updating a Web Page

In document Foundations Of Ajax (2006) pdf (Page 129-137)

As discussed previously, Ajax techniques are best applied when only a small portion of the page needs to change. In other words, use cases that were previously implemented using a complete page refresh to update a small portion of the page are fabulous candidates for applying Ajax techniques.

Consider the use case involving a single page into which the user enters information that is added to a list. In this example, you’ll see a Web page that lists the employees in an organiza- tion. The top of the page has three input boxes that accept the employee’s name, position, and department. Clicking the Add button submits the name, position, and department data to the server, where the employee information is added to the database.

Using traditional Web application techniques, the server would respond by re-creating the entire page, with the only difference from the previous page being that the new employee information is added to the list. In this example, you’ll use Ajax techniques to asynchronously submit the employee data to the server and insert the data into the database. The server will respond to the browser by sending a status code indicating the success or failure of the database operation. Assuming a successful database insert, the browser will use JavaScript DOM opera- tions to dynamically update the page content with the new employee information. This example also creates a Delete button so employee information can be deleted from the database.

Listing 4-13 shows the source code for the HTML Web page. The page has two sections: the first section consists of the input boxes that accept the employee name, title, and depart- ment and the Add button that initiates the database insert. The second section lists all the employees in the database, with each record having its own Delete button so the information can be deleted from the database.

Listing 4-13.employeeList.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Employee List</title> <script type="text/javascript"> var xmlHttp; var name; var title; var department; var deleteID;

var EMP_PREFIX = "emp-";

function createXMLHttpRequest() { if (window.ActiveXObject) { xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); } else if (window.XMLHttpRequest) { xmlHttp = new XMLHttpRequest(); } } function addEmployee() { name = document.getElementById("name").value; title = document.getElementById("title").value; department = document.getElementById("dept").value; action = "add";

if(name == "" || title == "" || department == "") { return;

}

var url = "EmployeeList?"

+ createAddQueryString(name, title, department, "add") + "&ts=" + new Date().getTime();

createXMLHttpRequest();

xmlHttp.onreadystatechange = handleAddStateChange; xmlHttp.open("GET", url, true);

xmlHttp.send(null); }

function createAddQueryString(name, title, department, action) { var queryString = "name=" + name

+ "&title=" + title + "&department=" + department + "&action=" + action; return queryString; } function handleAddStateChange() { if(xmlHttp.readyState == 4) { if(xmlHttp.status == 200) { updateEmployeeList(); clearInputBoxes(); } else {

alert("Error while adding employee."); } } } function clearInputBoxes() { document.getElementById("name").value = ""; document.getElementById("title").value = ""; document.getElementById("dept").value = ""; } function deleteEmployee(id) { deleteID = id;

var url = "EmployeeList?" + "action=delete" + "&id=" + id

createXMLHttpRequest();

xmlHttp.onreadystatechange = handleDeleteStateChange; xmlHttp.open("GET", url, true);

xmlHttp.send(null); }

function updateEmployeeList() {

var responseXML = xmlHttp.responseXML;

var status = responseXML.getElementsByTagName("status")

.item(0).firstChild.nodeValue; status = parseInt(status);

if(status != 1) { return; }

var row = document.createElement("tr");

var uniqueID = responseXML.getElementsByTagName("uniqueID")[0]

.firstChild.nodeValue; row.setAttribute("id", EMP_PREFIX + uniqueID);

row.appendChild(createCellWithText(name)); row.appendChild(createCellWithText(title)); row.appendChild(createCellWithText(department));

var deleteButton = document.createElement("input"); deleteButton.setAttribute("type", "button"); deleteButton.setAttribute("value", "Delete");

deleteButton.onclick = function () { deleteEmployee(uniqueID); }; cell = document.createElement("td"); cell.appendChild(deleteButton); row.appendChild(cell); document.getElementById("employeeList").appendChild(row); updateEmployeeListVisibility(); } function createCellWithText(text) {

var cell = document.createElement("td");

cell.appendChild(document.createTextNode(text)); return cell;

function handleDeleteStateChange() { if(xmlHttp.readyState == 4) { if(xmlHttp.status == 200) { deleteEmployeeFromList(); } else {

alert("Error while deleting employee."); } } } function deleteEmployeeFromList() { var status = xmlHttp.responseXML.getElementsByTagName("status") .item(0).firstChild.nodeValue; status = parseInt(status); if(status != 1) { return; }

var rowToDelete = document.getElementById(EMP_PREFIX + deleteID); var employeeList = document.getElementById("employeeList"); employeeList.removeChild(rowToDelete);

updateEmployeeListVisibility(); }

function updateEmployeeListVisibility() {

var employeeList = document.getElementById("employeeList"); if(employeeList.childNodes.length > 0) { document.getElementById("employeeListSpan").style.display = ""; } else { document.getElementById("employeeListSpan").style.display = "none"; } } </script> </head> <body> <h1>Employee List</h1>

<form action="#">

<table width="80%" border="0"> <tr>

<td>Name: <input type="text" id="name"/></td> <td>Title: <input type="text" id="title"/></td> <td>Department: <input type="text" id="dept"/></td> </tr>

<tr>

<td colspan="3" align="center">

<input type="button" value="Add" onclick="addEmployee();"/> </td>

</tr> </table> </form>

<span id="employeeListSpan" style="display:none;"> <h2>Employees:</h2>

<table border="1" width="80%"> <tbody id="employeeList"></tbody> </table>

</span> </body> </html>

Clicking the Add button initiates the database insert operation. The addEmployeefunction is called by the Add button’s onclickevent. The addEmployeefunction employs createAddQueryString

to build a query string containing the employee name, title, and department information entered by the user. After creating the XMLHttpRequest object and setting the onreadystatechangeevent handler, the request is submitted to the server.

Listing 4-14 details the Java servlet that handles this request. Upon receipt of the request, the servlet’s doGetmethod is called. This method retrieves the value of the query string’s action

parameter and directs the request to the appropriate method. In the case of an addition, the request is directed to the addEmployeemethod.

Listing 4-14.EmployeeListServlet.java package ajaxbook.chap4; import java.io.*; import java.net.*; import java.util.Random; import javax.servlet.*; import javax.servlet.http.*;

protected void addEmployee(HttpServletRequest request

, HttpServletResponse response) throws ServletException, IOException {

//Store the object in the database String uniqueID = storeEmployee();

//Create the response XML

StringBuffer xml = new StringBuffer("<result><uniqueID>"); xml.append(uniqueID);

xml.append("</uniqueID>"); xml.append("</result>");

//Send the response back to the browser sendResponse(response, xml.toString()); }

protected void deleteEmployee(HttpServletRequest request

, HttpServletResponse response) throws ServletException, IOException {

String id = request.getParameter("id");

/* Assume that a call is made to delete the employee from the database */

//Create the response XML

StringBuffer xml = new StringBuffer("<result>"); xml.append("<status>1</status>");

xml.append("</result>");

//Send the response back to the browser sendResponse(response, xml.toString()); }

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

String action = request.getParameter("action"); if(action.equals("add")) { addEmployee(request, response); } else if(action.equals("delete")) { deleteEmployee(request, response); } }

private String storeEmployee() {

/* Assume that the employee is saved to a database and the * database creates a unique ID. Return the unique ID to the * calling method. In this case, make up a unique ID. */

String uniqueID = "";

Random randomizer = new Random(System.currentTimeMillis()); for(int i = 0; i < 8; i++) {

uniqueID += randomizer.nextInt(9); }

return uniqueID; }

private void sendResponse(HttpServletResponse response, String responseText) throws IOException {

response.setContentType("text/xml"); response.getWriter().write(responseText); }

}

The addEmployeefunction is responsible for coordinating the database insertion and the server response. The addEmployeemethod delegates to the storeEmployeemethod to perform the actual database insert. In a real-world implementation, the storeEmployeemethod would likely call a database service that would handle the details of performing the database insert. In this simplified example, the storeEmployeefakes the database insert by generating a ran- dom unique ID that mimics the ID that a real database insert might return. The generated unique ID is then returned to the addEmployeemethod.

Assuming a successful database insertion, the addEmployeemethod then continues by preparing the response. The response is a simple XML string that returns a status code to the browser. The XML is created via string concatenation and then written to the response’s out- put stream.

The browser handles the server’s response by calling the handleAddStateChangemethod. This method is called whenever the XMLHttpRequest object signals a change in its internal ready state. Once the readystateproperty indicates that the server response has completed successfully, the updateEmployeeListfunction is called, followed by the clearInputBoxes

function. The updateEmployeeListfunction is responsible for adding the successfully inserted employee information to the list of employees that appears on the page. The clearInputBoxes

function is a simple utility method that clears out the input boxes so they’re ready for the next employee.

The updateEmployeeListfunction adds a row to the table that lists the employee informa- tion. It starts by using the document.createElementmethod to create an instance of a table row. The row’s idattribute is set to a value that includes the unique ID generated by the database insert. The idattribute value uniquely identifies the table row so that it can be easily removed from the table if the Delete button is ever used.

The updateEmployeeListfunction uses a utility function named createCellWithTextto create table cell elements that contain the specified text. The createCellWithTextfunction

creates table cells for the employee name, title, and department information entered by the user. Each cell is then added to the previously created table row.

The last item to create is the Delete button and the table cell that contains it. You can cre- ate the Delete button by using the document.createElementmethod to create a generic input element whose typeand valueattributes are set to buttonand Delete, respectively. You then create a table cell to house the Delete button and add the Delete button as a child element to the table cell. You then add the cell to the table row and add the row, which now contains cells for the employee name, title, department, and Delete button to the employee list table.

Deleting an employee works much the same way as adding an employee. The Delete but- ton’s onclickevent handler calls the deleteEmployeefunction, passing the employee’s unique ID to the function. A simple query string is created indicating the desired action (delete) and the unique ID of the employee record to delete. The request is submitted after the XMLHttpRequest object’s onreadystatechangeproperty is set to the desired event handler.

The EmployeeListServletservlet uses the deleteEmployeemethod to handle the employee delete use case. This simplified example assumes that another method handles the details of actually performing the database delete. Assuming a successful database delete, the deleteEmployeemethod prepares the XML string that is returned to the browser. Like the employee add use case, this use case returns a status code to the browser. Once created, the XML string is written back to the browser through the response object’s output stream.

The browser handles the server response through the handleDeleteStateChangefunc- tion, which forwards to the deleteEmployeeFromListmethod if the response is successful. The deleteEmployeeFromListfunction retrieves the status code from the XML response; the function immediately exits if the status code indicates an unsuccessful delete. Assuming a successful delete operation, the function continues by retrieving the table row representing the deleted information by using the document.getElementByIdmethod. The row is then deleted from the table body using the table body’s removeChildmethod.

WHY ISN’T THE SETATTRIBUTE METHOD USED

In document Foundations Of Ajax (2006) pdf (Page 129-137)