Saturday, December 17, 2005

AJAX

I used to create web pages using a form which has a target in a hidden iframe. When the page is loaded. The data in the returned page is then parsed into the form using javascript. There is a problem with it as there is no way I can tell whether the data retrieval is available.

Recently I read about AJAX (Asynchronous JavaScript and XML). It seems to address the above problem. The following is a simple script that I used to get it working. It has three parts.

1. Javascript.
2. HTML form (omitted here)
3. PHP (server side script)

1. Javascript

Include the following two javascript functions in your calling page

//-------------------------------------------------------------------------------
function doget(query,query1){
Query = "mypage.php?department="+query+"&bft="+query1
try
req = new ActiveXObject("Msxml2.XMLHTTP.3.0");
catch (e)
try
req = new ActiveXObject("Microsoft.XMLHTTP");
catch (e)
try
req = new XMLHttpRequest()
catch(e)
req = false;


if (req){
req.onreadystatechange = processReqChange;
req.open("GET", query, true);
req.setRequestHeader('Content-Type','text/xml');
req.send();
}
else
alert("Cannot start XmlHttpRequest Object")
}

//-------------------------------------------------------------------------------

function processReqChange() {
// only if req shows "complete"
if (req.readyState == 4) {
// only if "OK"
if (req.status == 200) {
dbhtml="<table border=1 cellspacing=0><tr><th>Day</th><th>No of failure</th><th>No Calls</th><th>%</th></tr>"
response = req.responseXML.documentElement;
xdatalength=response.getElementsByTagName('day').length
for (x=0;x<=xdatalength-1;x++){
dateday = response.getElementsByTagName('day')[x].firstChild.data;
statcnt = response.getElementsByTagName('count')[x].firstChild.data;
statmiss = response.getElementsByTagName('miss')[x].firstChild.data;

dbhtml=dbhtml+"<tr><td>"+dateday+"</td><td>"+statmiss+"</td><td>"+statcn
t+"</td><td>"+Math.round((statmiss*1)/(statcnt*1)*100)+"</td></tr>"
}
// a DIV tag with a name "mydiv" must be defined for this to work properly
document.myform.mydiv.innerHTML=dbhtml+</table>
}
else
alert("There was a problem retrieving the XML data:\n" + req.statusText);
}

}
//-------------------------------------------------------------------------------

2. HTML

Just any html form that has a clickable item with has a "onclick" propert to initiate the "doget" function in the javascript. The "processReqChange" function is a callback function (automatically invoked by AJAX).

3. PHP

The php script is as follows (the database query section is omitted).
The first 4 lines must be at the top of the php page.

<?php
header('Content-Type: text/xml');
echo '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
echo "<response>";

/// your sql statement here using the passed parameters

while (odbc_fetch_row($rs))
{
$statcnt = odbc_result($rs,"statcnt");
$statmiss = odbc_result($rs,"statmiss");
$clsdate = odbc_result($rs,"cdate");
echo "<day>".$clsdate."</day>";
echo "<count>".$statcnt."</count>";
echo "<miss>".$statmiss."</miss>";
}
echo "</response>";
?>


Using ColdFusion instead of PHP is very simple... Just convert the php page to ColdFusion to return the same information. No need to change the calling page.

Note also that req.open in the above example is made using "GET"
method. If you need to send a lot of parameters (like submitting a form to be saved into database, then use "POST" method. The req.send function then must contain your parameters as a string. E.g.
req.send("department=xx&bft=yy")

Another thing to note is that if you are saving the submitted data then you don't have to use "req.responseXML.documentElement". Just use "alert(req.responseText)" to send a windows messge to user on the status of saving. Your PHP can forget about constructing the XML. Just "echo" some messge upon success or failure.

Note that the above is not synchronous but it is not multithreaded either. It only works on one particular way thus it cannot be used in multiple process to get data.