There is a lot to say when it comes to AJAX!
I will take You through one of the Ajax calls I did in a sales application I did for a customer earlier this year and cut out unnecessary bits and pieces from the code. The sales application had one main document containing information about the customer and general stuff, and then the user added one product document for each product that should be included in the offer. Each product document could have different discount values, but in the main document they wanted a summary to be displayed of the total gross, net and discount values for the whole offer.
So, each time I wanted to update this summary, I made an Ajax call to get the values. Here is the call for retreiving the summary:
var xmlHttp = null;
function initAJAX()
{
return new ajaxRequest();
}
function ajaxRequest()
{
//'IE
var localXmlHttp = null;
try {
localXmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
localXmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e2) {
localXmlHttp = false;
}
}
//All the rest
if (!localXmlHttp && typeof XMLHttpRequest != 'undefined') {
localXmlHttp = new XMLHttpRequest();
}
return localXmlHttp;
}
function getSummary() {
//alert('start');
var docid = document.getElementById('DocID').value;
var sURL = '';
if (docid == '') {
return false;
}
sURL = getServerName() + '/' + dbPath + '/(ajaxGetSPASummary)?OpenAgent&id=' + docid;
try {
if (xmlHttp == null) {
xmlHttp = initAJAX();
}
var d = new Date();
//NOTE THIS! By adding a date/time stamp to the ajax call will make the browser to ignore any cached result!
//This will make every URL unique, and no cache being used...
xmlHttp.open("GET",sURL + '&dummy=' + d.getTime(),true);
xmlHttp.onreadystatechange = summarydone
xmlHttp.send(null);
}
catch(e) {
alert(e.message);
}
}
function summarydone() {
// This function could be embedded directly at the onreadystatechange above, but I think
// that doing like this makes the code more readable.
if (typeof(xmlHttp) !='undefined') {
if (xmlHttp.readyState==4) {
if (xmlHttp.status!=200) {
alert('Error checking for status : ' + xmlHttp.status);
} else {
summaryFinalize();
}
}
}
}
It would trigger an agent called 'ajaxGetSPASummary'. Here is the agent:
On Error Goto errorhandler Dim session As New NotesSession
Dim db As NotesDatabase
Dim doc As NotesDocument
Dim strQS As String
Dim strXML As String
Dim sFormula As String
Dim vEval As Variant
Dim dblGrossSales As Double
Dim dblNetSales As Double
Dim dblDiscount As Double
Set db = session.CurrentDatabase
Set doc = session.DocumentContext
strQS = doc.Query_String_Decoded(0)
strQS = Strright(strQS,"&id=")
strQS = Strleft(strQS,"&")
If strQS = "" Then
Exit Sub
End If sFormula = |@Sum(@DbLookup("":"NoCache";"":"";"(LookupProductsByDocId)";"| + strQS + |";"GrossSales"))|
vEval = Evaluate(sFormula)
If Isempty(vEval) Then
dblGrossSales = 0
Else
dblGrossSales = CDbl(vEval(0))
End If sFormula = |@Sum(@DbLookup("":"NoCache";"":"";"(LookupProductsByDocId)";"| + strQS + |";"NetSales"))|
vEval = Evaluate(sFormula)
If Isempty(vEval) Then
dblNetSales = 0
Else
dblNetSales = CDbl(vEval(0))
End If dblDiscount = Round((dblGrossSales - dblNetSales) / dblGrossSales,4)*100
strXML = |<?xml version='1.0' encoding='ISO-8859-1'?>| & Chr$(13)
strXML = strXML & |<summary>|&Chr$(13)
strXML = strXML & |<docid>| & strQS & |</docid>| & Chr$(13)
strXML = strXML & |<gross>| & Format$(dblGrossSales,"0") & |</gross>| & Chr$(13)
strXML = strXML & |<net>| & Format$(dblNetSales,"0") & |</net>| & Chr$(13)
strXML = strXML & |<discount>| & Format$(dblDiscount,"0") & |</discount>| & Chr$(13)
strXML = strXML & |</summary| & Chr$(13)agt_done:
Print |Content-Type:text/xml| & Chr$(13) ' Important! This must be the first printed line in the agent!
Print strXML
Exit Suberrorhandler:
Call LogError()
strXML = |<?xml version="1.0" ?>| & Chr$(13)
strXML = strXML & |<product>|&Chr$(13)
strXML = strXML & |<error code="| & Cstr(Err) & |" line="| & Cstr(Erl) & |">| & Error$ & |</error>| & Chr$(13)
strXML = strXML & |</product>|
Resume agt_done
Note two things in the agent:
1. The result of the agent is printed in XML format and we override the default creation of HTML-tags that all Domino agents do by printing the content type with
Print |Content-Type:text/xml| & Chr$(13)
This line must be the first printed line in the agent or Domino will produce the HTML tags!
2. The error is printed in XML format with attributes. I do not recommend using attributes when working with XML but I'm only doing it here for demonstration purposes.
So, when the agent is done, the JavaScript will call a JavaScript function called 'summaryFinalize()'. The global JavaScript variable/object called xmlHttp holds the result from the agent. You can now examine the XML-output by alerting the responsetext by:
alert(xmlHttp.responseText);
But since the output is not easy to read, we need to process the XML. Let's start with grabbing the first element in the XML-file:
var root = xmlHttp.responseXML.getElementsByTagName('summary').item(0);
If we don't get a root object then we can not continue, therefore we add errorhandling:
if (root == null) {
//Add alert or anything to inform user that something bad has happened!!
return;
}
Now we are ready to step through all the elements in the XML file. For each node, we examine the name of the node and in a JavaScript switch() we will do differently depending on which node we are currently processing. For each node, we are printing out the result in a corresponding HTML field on the form. Like, the 'gross' element will be written to a HTML field called 'dspGrossSales etc...
for (var iNode = 0; iNode < root.childNodes.length; iNode++) {
var node = root.childNodes.item(iNode);
switch(node.nodeName) {
case 'error':
var ecode = node.getAttribute("code"); // JavaScript is case sensitive! It should be '.getAttribute()' and NOT '.GetAttribute()'
var eline = node.getAttribute("line");
var etext = node.firstChild.nodeValue;
var strHtml = '<div class="error"><h1>An error occured</h1><p>Unable to process the request due to the following error:</p><p class="errortext">';
strHtml += 'Errorcode: ' + ecode + '<br />Errorline: ' + eline + '<br />Description: ' + etext + '<br /></p></div>';
var objError = document.getElementById('Message');
If (objError != null) {
objError.innerHTML = strHtml;
}
break;
case 'gross':
obj = document.getElementById('dspGrossSales');
if (obj != null) {
obj.value = node.firstChild.nodeValue;
}
break;
case 'net':
obj = document.getElementById('dspNetSales');
if (obj != null) {
obj.value = node.firstChild.nodeValue;
}
break;
case 'discount':
obj = document.getElementById('dspDiscount');
if (obj != null) {
obj.value = node.firstChild.nodeValue;
}
break;
}
}
Here is the whole JavaScript function 'summaryFinalize()':
function summaryFinalize() {
//alert(xmlHttp.responseText);
var root = xmlHttp.responseXML.getElementsByTagName('summary').item(0);
var obj;
if (root == null) {
return;
}
for (var iNode = 0; iNode < root.childNodes.length; iNode++) {
var node = root.childNodes.item(iNode);
switch(node.nodeName) {
case 'error':
var ecode = node.getAttribute("code");
var eline = node.getAttribute("line");
var etext = node.firstChild.nodeValue;
var strHtml = '<div class="error"><h1>An error occured</h1><p>Unable to process the request due to the following error:</p><p class="errortext">';
strHtml += 'Errorcode: ' + ecode + '<br />Errorline: ' + eline + '<br />Description: ' + etext + '<br /></p></div>';
var objError = document.getElementById('Message');
If (objError != null) {
objError.innerHTML = strHtml;
}
break;
case 'gross':
obj = document.getElementById('dspGrossSales');
if (obj != null) {
obj.value = node.firstChild.nodeValue;
}
break;
case 'net':
obj = document.getElementById('dspNetSales');
if (obj != null) {
obj.value = node.firstChild.nodeValue;
}
break;
case 'discount':
obj = document.getElementById('dspDiscount');
if (obj != null) {
obj.value = node.firstChild.nodeValue;
}
break;
}
}
}
Now, what if the XML file is more complicated that this? My XML file only contains one level of nodes. What if a node contains subnodes which contains subnodes etc...?
Then You have to make a for loop inside the for loop... This is nothing I'm covering in this article but I think that once You understand the XML format, it will not be any problems for You to walk through the nodes.