1. Trang chủ
  2. » Công Nghệ Thông Tin

Publishing AJAX and PHP - part 22 pdf

10 116 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 10
Dung lượng 1,39 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

212 // the id of the grid div var gridDivId = "gridDiv"; // the grid of the status div var statusDivId = "statusDiv"; // stores temporary row data var tempRow; // the ID of the product b

Trang 1

210

// clear any output that has already been generated

ob_clean();

// output the error message

$error_message = 'ERRNO: ' $errNo chr(10)

'TEXT: ' $errStr chr(10)

'LOCATION: ' $errFile

', line ' $errLine;

echo $error_message;

// prevent processing any more PHP scripts

exit;

}

?>

7 It's time for the client now Start by creating index.html:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html>

<head>

<title>AJAX Grid</title>

<script type="text/javascript" src="grid.js"></script>

<link href="grid.css" type="text/css" rel="stylesheet"/> </head>

<body onload="init();">

<div id="gridDiv" />

</body>

</html>

8 Now let's create the XSLT file named grid.xsl that will be used in the JavaScript code to generate the output:

<?xml version="1.0" encoding="ISO-8859-1"?>

<xsl:stylesheet version="1.0"

xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">

<h2>AJAX Grid</h2>

<xsl:call-template name="menu"/>

<form id="grid_form_id">

<table class="list">

<tr>

<th class="th1">ID</th>

<th class="th2">Name</th>

<th class="th3">Price</th>

<th class="th4">Promo</th>

<th class="th5"></th>

</tr>

<xsl:for-each select="data/grid/row">

<xsl:element name="tr">

<xsl:attribute name="id">

<xsl:value-of select="product_id" />

</xsl:attribute>

<td><xsl:value-of select="product_id" /></td>

<td><xsl:value-of select="name" /> </td>

<td><xsl:value-of select="price" /></td>

<td>

<xsl:choose>

<xsl:when test="on_promotion &gt; 0">

<input type="checkbox" name="on_promotion" disabled="disabled" checked="checked"/> </xsl:when>

<xsl:otherwise>

<input type="checkbox" name="on_promotion"

disabled="disabled"/>

</xsl:otherwise>

</xsl:choose>

</td>

Trang 2

Chapter 8

<td>

<xsl:element name="a">

<xsl:attribute name = "href">#</xsl:attribute>

<xsl:attribute name = "onclick">

editId(<xsl:value-of select="product_id" />, true) </xsl:attribute>

Edit

</xsl:element>

</td>

</xsl:element>

</xsl:for-each>

</table>

</form>

<xsl:call-template name="menu" />

</xsl:template>

<xsl:template name="menu">

<xsl:for-each select="data/params">

<table>

<tr>

<td class="left">

<xsl:value-of select="items_count" /> Items

</td>

<td class="right">

<xsl:choose>

<xsl:when test="previous_page>0">

<xsl:element name="a" >

<xsl:attribute name="href" >#</xsl:attribute>

<xsl:attribute name="onclick">

loadGridPage(<xsl:value-of select="previous_page"/>) </xsl:attribute>

Previous page

</xsl:element>

</xsl:when>

</xsl:choose>

</td>

<td class="left">

<xsl:choose>

<xsl:when test="next_page>0">

<xsl:element name="a">

<xsl:attribute name = "href" >#</xsl:attribute>

<xsl:attribute name = "onclick">

loadGridPage(<xsl:value-of select="next_page"/>) </xsl:attribute>

Next page

</xsl:element>

</xsl:when>

</xsl:choose>

</td>

<td class="right">

page <xsl:value-of select="returned_page" />

of <xsl:value-of select="total_pages" />

</td>

</tr>

</table>

</xsl:for-each>

</xsl:template>

</xsl:stylesheet>

9 Create grid.js:

// stores the reference to the XMLHttpRequest object

var xmlHttp = createXmlHttpRequestObject();

// the name of the XSLT file

var xsltFileUrl = "grid.xsl";

// the file that returns the requested data in XML format

var feedGridUrl = "grid.php";

Trang 3

212

// the id of the grid div

var gridDivId = "gridDiv";

// the grid of the status div

var statusDivId = "statusDiv";

// stores temporary row data

var tempRow;

// the ID of the product being edited

var editableId = null;

// the XSLT document

var stylesheetDoc;

// eveything starts here

function init()

{

// test if user has browser that supports native XSLT functionality if(window.XMLHttpRequest && window.XSLTProcessor && window.DOMParser) {

// load the grid

loadStylesheet();

loadGridPage(1);

return;

}

// test if user has Internet Explorer with proper XSLT support

if (window.ActiveXObject && createMsxml2DOMDocumentObject())

{

// load the grid

loadStylesheet();

loadGridPage(1);

// exit the function

return;

}

// if browser functionality testing failed, alert the user

alert("Your browser doesn't support the necessary functionality."); }

function createMsxml2DOMDocumentObject()

{

// will store the reference to the MSXML object

var msxml2DOM;

// MSXML versions that can be used for our grid

var msxml2DOMDocumentVersions = new Array("Msxml2.DOMDocument.6.0", "Msxml2.DOMDocument.5.0", "Msxml2.DOMDocument.4.0"); // try to find a good MSXML object

for (var i=0; i<msxml2DOMDocumentVersions.length && !msxml2DOM; i++) {

try

{

// try to create an object

msxml2DOM = new ActiveXObject(msxml2DOMDocumentVersions[i]); }

catch (e) {}

}

// return the created object or display an error message

if (!msxml2DOM)

alert("Please upgrade your MSXML version from \n" +

"http://msdn.microsoft.com/XML/XMLDownloads/default.aspx"); else

return msxml2DOM;

}

// creates an XMLHttpRequest instance

function createXmlHttpRequestObject()

{

// will store the reference to the XMLHttpRequest object

var xmlHttp;

Trang 4

Chapter 8

// this should work for all browsers except IE6 and older

try

{

// try to create XMLHttpRequest object

xmlHttp = new XMLHttpRequest();

}

catch(e)

{

// assume IE6 or older

var XmlHttpVersions = new Array("MSXML2.XMLHTTP.6.0",

"MSXML2.XMLHTTP.5.0",

"MSXML2.XMLHTTP.4.0",

"MSXML2.XMLHTTP.3.0",

"MSXML2.XMLHTTP",

"Microsoft.XMLHTTP");

// try every prog id until one works

for (var i=0; i<XmlHttpVersions.length && !xmlHttp; i++)

{

try

{

// try to create XMLHttpRequest object

xmlHttp = new ActiveXObject(XmlHttpVersions[i]);

}

catch (e) {}

}

}

// return the created object or display an error message

if (!xmlHttp)

alert("Error creating the XMLHttpRequest object.");

else

return xmlHttp;

}

// loads the stylesheet from the server using a synchronous request

function loadStylesheet()

{

// load the file from the server

xmlHttp.open("GET", xsltFileUrl, false);

xmlHttp.send(null);

// try to load the XSLT document

if (this.DOMParser) // browsers with native functionality

{

var dp = new DOMParser();

stylesheetDoc = dp.parseFromString(xmlHttp.responseText, "text/xml"); }

else if (window.ActiveXObject) // Internet Explorer?

{

stylesheetDoc = createMsxml2DOMDocumentObject();

stylesheetDoc.async = false;

stylesheetDoc.load(xmlHttp.responseXML);

}

}

// makes asynchronous request to load a new page of the grid

function loadGridPage(pageNo)

{

// disable edit mode when loading new page

editableId = false;

// continue only if the XMLHttpRequest object isn't busy

if (xmlHttp && (xmlHttp.readyState == 4 || xmlHttp.readyState == 0)) {

var query = feedGridUrl + "?action=FEED_GRID_PAGE&page=" + pageNo; xmlHttp.open("GET", query, true);

xmlHttp.onreadystatechange = handleGridPageLoad;

xmlHttp.send(null);

}

}

Trang 5

214

// handle receiving the server response with a new page of products function handleGridPageLoad()

{

// when readyState is 4, we read the server response

if (xmlHttp.readyState == 4)

{

// continue only if HTTP status is "OK"

if (xmlHttp.status == 200)

{

// read the response

response = xmlHttp.responseText;

// server error?

if (response.indexOf("ERRNO") >= 0

|| response.indexOf("error") >= 0

|| response.length == 0)

{

// display error message

alert(response.length == 0 ? "Server serror." : response); // exit function

return;

}

// the server response in XML format

xmlResponse = xmlHttp.responseXML;

// browser with native functionality?

if (window.XMLHttpRequest && window.XSLTProcessor &&

window.DOMParser)

{

// load the XSLT document

var xsltProcessor = new XSLTProcessor();

xsltProcessor.importStylesheet(stylesheetDoc);

// generate the HTML code for the new page of products

page = xsltProcessor.transformToFragment(xmlResponse, document); // display the page of products

var gridDiv = document.getElementById(gridDivId);

gridDiv.innerHTML = "";

gridDiv.appendChild(page);

}

// Internet Explorer code

else if (window.ActiveXObject)

{

// load the XSLT document

var theDocument = createMsxml2DOMDocumentObject();

theDocument.async = false;

theDocument.load(xmlResponse);

// display the page of products

var gridDiv = document.getElementById(gridDivId);

gridDiv.innerHTML = theDocument.transformNode(stylesheetDoc); }

}

else

{

alert("Error reading server response.")

}

}

}

// enters the product specified by id into edit mode if editMode is true, // and cancels edit mode if editMode is false

function editId(id, editMode)

{

// gets the <tr> element of the table that contains the table

var productRow = document.getElementById(id).cells;

// are we enabling edit mode?

if(editMode)

{

// we can have only one row in edit mode at one time

Trang 6

Chapter 8

if(editableId) editId(editableId, false);

// store current data, in case the user decides to cancel the changes save(id);

// create editable text boxes

productRow[1].innerHTML =

'<input class="editName" type="text" name="name" ' +

'value="' + productRow[1].innerHTML+'">';

productRow[2].innerHTML =

'<input class="editPrice" type="text" name="price" ' +

'value="' + productRow[2].innerHTML+'">';

productRow[3].getElementsByTagName("input")[0].disabled = false; productRow[4].innerHTML = '<a href="#" ' +

'onclick="updateRow(document.forms.grid_form_id,' + id +

')">Update</a><br/><a href="#" onclick="editId(' + id +

',false)">Cancel</a>';

// save the id of the product being edited

editableId = id;

}

// if disabling edit mode

else

{

productRow[1].innerHTML = document.forms.grid_form_id.name.value; productRow[2].innerHTML = document.forms.grid_form_id.price.value; productRow[3].getElementsByTagName("input")[0].disabled = true; productRow[4].innerHTML = '<a href="#" onclick="editId(' + id + ',true)">Edit</a>';

// no product is being edited

editableId = null;

}

}

// saves the original product data before editing row

function save(id)

{

// retrieve the product row

var tr = document.getElementById(id).cells;

// save the data

tempRow = new Array(tr.length);

for(var i=0; i<tr.length; i++)

tempRow[i] = tr[i].innerHTML;

}

// cancels editing a row, restoring original values

function undo(id)

{

// retrieve the product row

var tr = document.getElementById(id).cells;

// copy old values

for(var i=0; i<tempRow.length; i++)

tr[i].innerHTML = tempRow[i];

// no editable row

editableId = null;

}

// update one row in the grid if the connection is clear

function updateRow(grid, productId)

{

// continue only if the XMLHttpRequest object isn't busy

if (xmlHttp && (xmlHttp.readyState == 4 || xmlHttp.readyState == 0)) {

var query = feedGridUrl + "?action=UPDATE_ROW&id=" + productId + "&" + createUpdateUrl(grid);

xmlHttp.open("GET", query, true);

xmlHttp.onreadystatechange = handleUpdatingRow;

xmlHttp.send(null);

}

}

Trang 7

216

// handle receiving a response from the server when updating a product function handleUpdatingRow()

{

// when readyState is 4, we read the server response

if(xmlHttp.readyState == 4)

{

// continue only if HTTP status is "OK"

if(xmlHttp.status == 200)

{

// read the response

response = xmlHttp.responseText;

// server error?

if (response.indexOf("ERRNO") >= 0

|| response.indexOf("error") >= 0

|| response.length == 0)

alert(response.length == 0 ? "Server serror." : response); // if everything went well, cancel edit mode

else

editId(editableId, false);

}

else

{

// undo any changes in case of error

undo(editableId);

alert("Error on server side.");

}

}

}

// creates query string parameters for updating a row

function createUpdateUrl(grid)

{

// initialize query string

var str = "";

// build a query string with the values of the editable grid elements for(var i=0; i<grid.elements.length; i++)

switch(grid.elements[i].type)

{

case "text":

case "textarea":

str += grid.elements[i].name + "=" +

escape(grid.elements[i].value) + "&"; break;

case "checkbox":

if (!grid.elements[i].disabled)

str += grid.elements[i].name + "=" +

(grid.elements[i].checked ? 1 : 0) + "&";

break;

}

// return the query string

return str;

}

10 Finally, create grid.css:

body

{

font-family: Verdana, Arial;

font-size: 10pt

}

table

{

width: 500px;

}

td.right

{

Trang 8

Chapter 8

color: darkblue;

text-align: right;

width: 125px

}

td.left

{

color: darkblue;

text-align: left;

width: 125px

}

table.list

{

border: black 1px solid;

}

th

{

text-align: left;

background-color: navy;

color: white

}

th.th1

{

width: 30px

}

th.th2

{

width: 300px

}

input.editName

{

border: black 1px solid;

width: 300px

}

input.editPrice

{

border: black 1px solid;

width: 50px

}

11 Load http://localhost/ajax/grid in your web browser, and test its functionality

to make sure it works as expected (see Figures 8.1 and 8.2 for reference)

What Just Happened?

Let's dissect the code starting with the server-side functionality At the heart of the server lies the database In our case, we have a table called product with the following fields:

• product_id is the table's primary key, containing the numeric ID of the product

• name is the product's name

• price is the product's price

• on_promotion is a bit field (should only take values of 0 or 1, although MySQL may permit more, depending on the version), which specifies if the product is on

promotion We used this field for our grid because it allows us to show how to use a checkbox to display the bit value

Trang 9

218

As usual on the server, we have a PHP script, which in this case is named grid.php, that is the main access point for all asynchronous client requests

grid.php expects to receive a query string parameter called action that tells it what action it is expected to perform The possible values are:

• FEED_GRID_PAGE: This value is used to retrieve a page of products Together with this parameter, the server also expects a parameter named page, which specifies what page of products to return

• UPDATE_ROW: This value is used to update the details of a row that was edited by the user For this action, the server also expects to receive the new values for the

product, in four parameters named id, name, price, and on_promotion

To see the data generated by the server, make a simple call to http://localhost/ajax/grid/ grid.php?action=FEED_GRID_PAGE&page=1 Using the default database information, the output will look like Figure 8.3:

Figure 8.3: Server Returning the First Page of Products

Trang 10

Chapter 8

On the client, this data will be parsed and transformed to the HTML grid using an XSL

transformation This code was tested with Mozilla and Internet Explorer, which at the time of writing supported the required functionality Opera is expected to support XSL Transformations starting with version 9

The XSL transformation code is defined in grid.xsl Please see Appendix C at

http://ajaxphp.packtpub.comfor a primer into the world of XSL, and refer one of the many available books and online resources for digging into the details XSL is a really big subject, so be prepared for a lot of learning if you intend to master it

The first function in the client script, grid.js, is init() This function checks if the user's browser has the necessary features to perform the XSL transformation:

// eveything starts here

function init()

{

// test if user has browser that supports native XSLT functionality

if(window.XMLHttpRequest && window.XSLTProcessor && window.DOMParser) {

// load the grid

loadStylesheet();

loadGridPage(1);

return;

}

// test if user has Internet Explorer with proper XSLT support

if (window.ActiveXObject && createMsxml2DOMDocumentObject())

{

// load the grid

loadStylesheet();

loadGridPage(1);

// exit the function

return;

}

// if browser functionality testing failed, alert the user

alert("Your browser doesn't support the necessary functionality.");

}

This function allows continuing if the browser is either Internet Explorer (in which case the user also needs a recent MSXML version), or a browser that natively supports the XMLHttpRequest,

XSLTProcessor, and DOMParser classes

The second function that is important to understand is loadStylesheet() This function is called once when the page loads, to request the grid.xsl file from the server, which is loaded locally The grid.xls file is loaded using a synchronous call, and then is stored using techniques specific

to the user's browser, depending on whether the browser has native functionality, or it is Internet Explorer, in which case an ActiveXObject is used:

// loads the stylesheet from the server using a synchronous request

function loadStylesheet()

{

// load the file from the server

xmlHttp.open("GET", xsltFileUrl, false);

xmlHttp.send(null);

// try to load the XSLT document

if (this.DOMParser) // browsers with native functionality

{

var dp = new DOMParser();

stylesheetDoc = dp.parseFromString(xmlHttp.responseText, "text/xml"); }

else if (window.ActiveXObject) // Internet Explorer?

Ngày đăng: 04/07/2014, 17:20