AJAX – What’s The Buzz – part two (slightly more in depth, on challenges and solutions)

In a previous article (AJAX – What’s the Buzz Part 1), we discussed the introduction to AJAX as a Design Pattern. We saw a simple demonstration leading up to why we might need something like AJAX ‘mechanisms’ and how these can be implemented. The article was basically a report from the session and workshop we had last week at AMIS. In this article, I will continue with the report, telling you about the discussion and brainstom we had, as well as the more advanced examples of AJAX usage.

We had discussions on what would we use Ajax for in the type of web applications we typically develop, what are the additional challenges using Ajax will have us face and how do we tackle some obivous technical issues such as multiple parallel requests, repeating requests (for tickers or pollers) and requests to other domains than the one the base document was loaded from (the cross domain browser issue). We also discussed a number of libraries and frameworks that may help us in doing Ajax style development. Note however that most of these frameworks are primarily JavaScript libraries, not necessarily Ajax libraries.

Hello World in Ajax programming

Where would we be without Hello World – Ajax style? So here we go – simplest Ajax application man can come up with. Or at least close to it. A very simple HTML document – we have left out the JavaScript.

<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
  <title>Simple XMLHttpRequest</title> 
  <script type="text/javascript"> …</script>
</head> 
<body> 
  <form action="#"> 
    <input type="button" value="Start Basic Asynchronous Request" onclick="startRequest();"/> 
  </form> 
</body>
</html>

When viewed in the browser, this page looks like:

AJAX - What's The Buzz - part two (slightly more in depth, on challenges and solutions)

(click on the image for a live demo)

Now let’s flesh out that piece of JavaScript: the startRequest() method:

<script type="text/javascript"> 
var xmlHttp; 

// this function initializes the xmlHttp variable in a cross browser fashion
function createXMLHttpRequest() { 
  if (window.ActiveXObject) { 
       xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); 
  } else if (window.XMLHttpRequest) { 
      xmlHttp = new XMLHttpRequest(); 
    }  
} 

// this function initiates the Ajax request (cycle) by first initializing the xmlHttp object 
// that contains the request and response, setting its main properties and sending the request
function startRequest() { 
  createXMLHttpRequest(); 
  // specify (callback) function to handle response
  xmlHttp.onreadystatechange = handleStateChange;
  // GET or POST, what is the URL, Asynch or Synch
  xmlHttp.open("GET", "simpleResponse.xml", true); 
  xmlHttp.send(null); 
} 

// the callback function that is called upon any change in the state of the xmlHttp object.
// Note: these state changes occur at various times during the Http request/response cycle.
function handleStateChange() {      
  ...
}//handleStateChange

The final piece of code is what gets called once the Ajax request-cycle is complete:

function handleStateChange() { 
  if ( xmlHttp.readyState == 4) { 
     if ( xmlHttp.status == 200) { 
        alert("The server replied with: " + xmlHttp.responseText); 
     } 
  } 
} 
</script>

Here the state changes in the xmlHttp object are tracked; once we get to status 4 (done) and code 200 (successful) we know we have a response in that we can start to process. The alert displayed in the browser after pressing the button and a very brief delay looks like this:

AJAX - What's The Buzz - part two (slightly more in depth, on challenges and solutions)

Now it does not get any simpler or less useful. But this is the bare essence of Ajax: it is asynchronous (the user had not idea you were doing communication with the web server in the background) and it is plain JavaScript.

DOM manipulation using the Ajax Response

Showing an alert with the response received to our Ajax request is probably one of the lousiest things we can do. If that is what Ajax was all about, you might as well stop reading right now. Of course there is much more than this. Responses to Ajax requests can be used in any way you like and the two most obvious usages are: store the result in JavaScript memory structures for later use or inject into the current page through DHTML to for direct presentation to the user. DHTML is another of those mythical concepts that on close inspections turns out to be not much more than programmatic access to the Browser’s DOM. That in itself is pretty terrific by the way (since IE 4.0 and Netscape 4.0, 1997 or so), but nothing changed in HTML or CSS or even in JavaScript – accept for some operations on the document object.

We will most definitely not embark on a broad discussion on DHTML or even the DOM, but we will take a look at a simple example of using DHTML or displaying the Ajax response to the user. We have the same page as before, with the humongous button. This time we will not be so crude as to display the Ajax response in an Alert. We will be sophisticated and use DHTML to show the response dynamically in our document. (note: you can click on the image for a live demo).

AJAX - What's The Buzz - part two (slightly more in depth, on challenges and solutions)

We have made two subtle changes to the HTML document: we have added a STYLE definition:

<style type="text/css">
    hidden  { display:none}
    visible  { display:block}
</style>

and we have inserted a DIV element – that for the moment is invisible -:

    <div id="content" style="background-color:pink;font-family:sans-serif" 
         class="hidden">
    </div>

The code responsible for handling the Ajax response in such a refined way is as follows:

 function el(id) {
  return document.getElementById(id);
}
    
function handleStateChange() {
    if(xmlHttp.readyState == 4) {
        if(xmlHttp.status == 200) {
          el('content').innerHTML = xmlHttp.responseText;
          el('content').setAttribute("className","visible");
        }
    }
}

Note that we have been somewhat lazy by just using the innerHtml property (once an IE only pollution of DHTML). There are more refined ways to programmatically create DOM elements, as we will see shortly.

Operations and properties on DOM elements that may come on handy include:

 

  • document.getElementById(id)
  • element.getElementsByTagName(tagName)
  • document.createElement(tagName)
  • document.createTextNode(text)
  • element.appendChild(childNode)
  • element.getAttribute(attributeName)
  • element.s
    etAttribute(attributeName, newValue)<
    /li>
  • element.insertBefore(newNode, targetNode)

Properties of DOM elements: parentNode, childNodes (Array of nodes), firstChild, lastChild, nextSibling, previousSibling, nodeValue

The next example shows an amalgation of two samples from the book Foundations of Ajax (see Resources). It illustrates a combination of an Ajax request to retrieve data depending on a selection made by the user with dynamically creating DOM nodes to display a table with these values. For a live demo of this example, see here: http://www.amis.nl//files/technology/ajaxBuzz/DHTMLCreatingNodes/parseXMLandDHTML.html.

AJAX - What's The Buzz - part two (slightly more in depth, on challenges and solutions)

This page contains two parts: the Ajax request and the DOM Manipulation. First the Ajax stuff:

var xmlHttp;
var requestType = "";

function createXMLHttpRequest() {
    if (window.ActiveXObject) {
        xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
    } 
    else if (window.XMLHttpRequest) {
        xmlHttp = new XMLHttpRequest();
    }
}
    
function startRequest(requestedList) {
    requestType = requestedList;
    createXMLHttpRequest();
    xmlHttp.onreadystatechange = handleStateChange;
    xmlHttp.open("GET", "parseXML.xml", true);
    xmlHttp.send(null);
}
    
function handleStateChange() {
    if(xmlHttp.readyState == 4) {
        if(xmlHttp.status == 200) {
            if(requestType == "north") {
                listStatesInRegion("north");
            }
            else if(requestType == "south") {
                listStatesInRegion("south");
            }
            else if(requestType == "all") {
                listAllStates();
            }
        }
    }
}

The startRequest() function is invoked from the onClick event handler on each of the three buttons, for example:

 <input type="button" value="View All Listed Northern States" onclick="startRequest('north');"/>

The table with the names of the selected states is (re)created dynamically when the XML data is received in the Ajax response:

function listStatesInRegion(region) {
    var xmlDoc = xmlHttp.responseXML;
    var regionNode = xmlDoc.getElementsByTagName(region)[0];
    var regionStates = regionNode.getElementsByTagName("state");
    parseResults(regionStates);    
}

function listAllStates() {
    var xmlDoc = xmlHttp.responseXML;
    var allStates = xmlDoc.getElementsByTagName("state");
    parseResults(allStates);    
}

function clearPreviousResults() {
    var header = document.getElementById("header");
    if(header.hasChildNodes()) {
        header.removeChild(header.childNodes[0]);
    }

    var tableBody = document.getElementById("resultsBody");
    while(tableBody.childNodes.length > 0) {
        tableBody.removeChild(tableBody.childNodes[0]);
    }
}

function parseResults(states) {
    clearPreviousResults();
    var state = null;
    var name = "";
    for(var i = 0; i < states.length; i++) {
        state = states[i];
        name = state.childNodes[0].nodeValue;
        
        addTableRow(name);        
    }
    
    var header = document.createElement("h2");
    var headerText = document.createTextNode("Results:");
    header.appendChild(headerText);
    document.getElementById("header").appendChild(header);    
    document.getElementById("resultsTable").setAttribute("border", "1");
}


function addTableRow(name) {
    var row = document.createElement("tr");
    var cell = createCellWithText(name);
    row.appendChild(cell);  
    document.getElementById("resultsBody").appendChild(row);
}

function createCellWithText(text) {
    var cell = document.createElement("td");
    var textNode = document.createTextNode(text);
    cell.appendChild(textNode);    
    return cell;
}

The relevant section of the HTML document is this fragment:

  <span id="header">
  </span>

  <table id="resultsTable" width="75%" border="0">
    <tbody id="resultsBody">
    </tbody>
  </table>

Bringing Server Side Validation to the Client

An example of how Ajax can be used to increase the interactivity of our web applications using Ajax is performing server side validation through asynchronous requests whenever the user has changed a field. In the next example, the end user can create a new Employee. One of the properties to be provided for new employees is their Job. However, even though we have not created a Select element for Job, there still is a restriction on the job title we can assign to new employees. We have linked the onChange event of the Job field to a JavaScript function that makes an Ajax request to a server side Servlet, passing the Job as entered by the User. The Servlet will validate the Job title using the same code that normally does the server side validation of this particular field. If the validation is successful- the response is ignored in the browser. However, when the value typed in by the user is not a valid one, a validation error will be prresented to the end user, within seconds or even a fraction of a second after leaving the Job field.

AJAX - What's The Buzz - part two (slightly more in depth, on challenges and solutions)

Now the validation error is presented:

AJAX - What's The Buzz - part two (slightly more in depth, on challenges and solutions)

The relevant JavaScript code in the HTML document:

 

var xmlHttp;

function createXMLHttpRequest() {
    if (window.ActiveXObject) {
        xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
    } 
    else if (window.XMLHttpRequest) {
        xmlHttp = new XMLHttpRequest();
    }
}
    
function startRequest(url) {
    createXMLHttpRequest();
    xmlHttp.onreadystatechange = handleStateChange;
    xmlHttp.open("GET", url, true);
    xmlHttp.send(null);
}
    
function el(id) {
  return document.getElementById(id);
}
    
function handleStateChange() {
    if(xmlHttp.readyState == 4) {
        if(xmlHttp.status == 200) {
          var response = xmlHttp.responseText;
          if (response.toLowerCase().indexOf("error")>-1) {
             el('errorbox').innerHTML = xmlHttp.responseText;
             el('errorbox').setAttribute("className","visible");
          }
          else {
            el('errorbox').innerHTML = '';
            el('errorbox').setAttribute("className","hidden");
          }
        }
    }
}
 
function validateJob(job) {
   startRequest('http://10.255.255.101:8988/AjaxBuzz-AjaxBuzz-context-root/validatejob?job='+job);
}
...
<div id="errorbox" class="hidden" style="background-color:rgb(255,0,0); color:rgb(255,255,0); font-family:sans-serif; font-size:large;"></div>        
...
<td width="21%">
            <h3>
         Job
            </h3></td>
        <td width="29%">
            <input type="text" id="job" name="job" size="20" onchange="validateJob(this.value);"/>
          </td>

The servlet that performs the validation – very simplistic of course:

package nl.amis.ajaxbuzz;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.*;
import javax.servlet.http.*;

public class ValidateJob extends HttpServlet {
    private static final String CONTENT_TYPE = "text/text; charset=windows-1252";

    public void init(ServletConfig config) throws ServletException {
        super.init(config);
    }

    final static String[] jobs = new String[]{"manager","consultant","expertise manager","cook","people manager","programmer","account manager","teacher"};

    private boolean notValid(String job) {
        for (int i=0;i<jobs.length;i++) {
          if (jobs[i].equalsIgnoreCase(job))
            return false;
        }
        return true;
    }

    public void doGet(HttpServletRequest request, 
                      HttpServletResponse response) throws ServletException, 
                                                           IOException {
        String job = "";
        job = request.getParameter("job");
        response.setContentType(CONTENT_TYPE);
        PrintWriter out = response.getWriter();
        if (notValid(job) ) {
          out.println("Validation Error in the value for Job: <i>"+job+ "</i> unfortunately is not a valid value! Please try again.");
        }
        else {
          out.println("Validation of Job (value="+job+"was successful.");                    
        }
        out.close();
    }
}

Discussion: what do we use Ajax for and what are the challenges it brings?

Having seen some examples of famous Ajax applications (see the first installment Ajax What’s the Buzz) and now having taken an more in depth look at what you can do and how you can do it, we discuss the potential for Ajax in our own applications. We typically develop data oriented OLTP style administrative web applications using Java/J2EE technology. How does Ajax fit in with that type of application?

Obvious usages of Ajax include:

  • Complex Validations – client side validations that are really server side validations
  • Suggestions (code completion)
  • Dynamic Lists of Values (list is adapted based on user action in the current page)
  • Trees – dynamic loading of expanded nodes
  • Client Side content aggregation and integration (collect content from various sources and combine in the client)
  • Post Loading large resources
  • Client Side Web Service calls
  • “Tickers” – repetitive, polling calls
  • Partial Page Refresh
  • Visual, dynamic enhancements
  • Fewer pages, more functionality per page

Although Ajax may seem very simple to implement, looks can deceive. Implementing a single Ajax request for a single page is quite innocent. However, moving towards a more dynamic, interactive, fewer-page-application requires a radical change in the way we design and implement applications. And typical server side frameworks like Struts and JSF may not be optimally equiped for this new approach. Some of the challenges associated with Ajax we discussed in our workshop:

  • Implement Professional Software Engineering with  JavaScript now that more of the application will be client side/browser based
  • Prototyping and Functional (interaction) Design – static HTML and frequent full page refresh will not do at all!
  • Respective Roles of Client and Server – what will happen where
  • Complexity of the application – for development & maintenance and for the end user!
  • Back Button – what does it mean to push the Browser’s back button in a Single Page Application?
  • Single Page Application!?
  • Danger of going completely overboard with Ajax: it can be done, so we will do it!

There does seem to be some good news on the first issue: JavaScript development is definitely getting more professional. More tools – IDEs, debuggers (Venkman), browser plugins (especially Firefox) – that better support JavaScript development, more and very professional libraries and frameworks and counterparts for key software engineering components in other technology realms, such as JSDoc, JSUnit (and Selenium) and JSLint.

However we still must take a lot of care, as well as hone our JavaScript programming skills, to make sure we work in a structured, well designed way with JavaScript. I am constantly surprised at the richness of the JS language – the way OO concepts can be applied, the flexibility and the dangers following from that flexibility.

 

Multiple, simultaneous or Parallel Requests – Introducing some structure to our Ajax world

So far, we have had very simple Ajax applications with only one Ajax request at the time. Or at least that was our assumption. In the validation example we saw before, if we had added a server side validation cycle for the Salary field and we had a quick typist, we might have run into problems: after leaving the changed Job field, an Ajax request would be sent, using the global xmlHttp variable. Then, if the user would change and leave the salary field before the response to this request was in, we would reuse this variable to fire another Ajax request, this time to validate the Salary. We would have lost our hold on the original Job validation request and would not be able to present a warning to end user if that validation failed.

Basically, we cannot handle more than one Ajax request at a time. Which of course is a severe limitation. So what can we do? Well, more structure, more encapsulation, a bit of OO and some inspiration from the Ajax in Action book. Following the example in the book, we create a very simple JavaScript library with a very simple ‘framework’ – it is so small it does not really deserve that name – for making Ajax requests. Two important characteristics: Each request is an Object instance in its own right (no more global variable) and the Callback function has direct access to the request object, instead of through the global variable.

The code in the ContentLoader.js library is somewhat terse – but it does the job:

/* namespacing object */
var net=new Object();

net.READY_STATE_UNINITIALIZED=0;
net.READY_STATE_LOADING=1;
net.READY_STATE_LOADED=2;
net.READY_STATE_INTERACTIVE=3;
net.READY_STATE_COMPLETE=4;


/*--- content loader object for cross-browser requests ---*/
net.ContentLoader=function(url,onload,onerror,method,params,contentType){
  this.req=null;
  this.onload=onload;
  this.onerror=(onerror) ? onerror : this.defaultError;
  this.loadXMLDoc(url,method,params,contentType);
  this.parameter = null;
}

net.ContentLoader.prototype.loadXMLDoc=function(url,method,params,contentType){
  if (!method){
    method="GET";
  }
  if (!contentType && method=="POST"){
    contentType='application/x-www-form-urlencoded';
  }
  if (window.XMLHttpRequest){
    this.req=new XMLHttpRequest();
  } else if (window.ActiveXObject){
    this.req=new ActiveXObject("Microsoft.XMLHTTP");
  }
  if (this.req){
    try{
      var loader=this;
      this.req.onreadystatechange=function(){
  net.ContentLoader.onReadyState.call(loader);
      }
      this.req.open(method,url,true);
      if (contentType){
        this.req.setRequestHeader('Content-Type', contentType);
      }
      this.req.send(params);
    }catch (err){
      this.onerror.call(this);
    }
  }
}

net.ContentLoader.onReadyState=function(){
  var req=this.req;
  var ready=req.readyState;
  if (ready==net.READY_STATE_COMPLETE){
    var httpStatus=req.status;
      if (httpStatus==200 || httpStatus==0){
        this.onload.call(this);
      }
      else {
        this.onerror.call(this);
      }
  }
}

net.ContentLoader.prototype.defaultError=function(){
  alert("error fetching data!"
    +"\n\nreadyState:"+this.req.readyState
    +"\nstatus: "+this.req.status
    +"\nheaders: "+this.req.getAllResponseHeaders());
}

The main “trick” in this piece of code is in the definition of the callback function on the XmlHttpRequest object:

var loader=this;
this.req.onreadystatechange=function(){
        net.ContentLoader.onReadyState.call(loader);
      }

Here we create a callback function on the fly. This function will make a call to the function onReadyState of the ContentLoader (prototype) object that in turns makes a call to the function specified in the onload property of the current ContentLoader object. In both calls, the ContentLoader object is passed as a parameter. Hence, each callback function has access to its own and private request object!

If we load this library in our HTML Document – like this:

<script src=”ContentLoader.js” type=”text/javascript”></script>

– we can easily create Ajax requests, as many as we like:

function doAjax(url, callback) {
  loader = new net.ContentLoader(url,callback);
}
function startRequests() {
  doAjax("https://technology.amis.nl/blog/postreads.php", processBlogStats);
  doAjax("empsInDept20.xml", processEmps);
  doAjax("http://www.oracle.com/technology", processOTN);
}
    
function processBlogStats() {
    alert("The server replied with the current total Postreads on the AMIS Weblog: " + this.req.responseText);
}

function el(id) {
  return document.getElementById(id);
}
    
function processEmps() {
  el('content').innerHTML = this.req.responseText;
  el('content').setAttribute("className","visible");
}
function processOTN() {
  el('content2').innerHTML = this.req.responseText;
  el('content2').setAttribute("className","visible");
}

In this example, we do three simultaneous Ajax requests, each with their own callback function, each request neatly encapsulated in its own ContentLoader object.

Repeating Requests for Pollers and Tickers

Sometimes, we use Ajax Requests for checking on a value – and displaying it in our page – that we know will change frequently. Think of counters, stock tickers, weather conditions, news headlines etc. Repetitiveness is not a characteristic of Ajax or Ajax Requests. However, the setTimeOut() system function is integral part of the JavaScript language and that is what we can use to have a request schedule itself again once completed.

I have taken the ContentLoader library we have introduced before and modified it to suit my current needs. I have extended the ContentLoader object, to also contain a property refreshInterval. When set, the request associated with the ContentLoaded object will be scheduled to be re-executed when it has completed. This functionality is found in the last lines of function startProcessing().  Note by the way that this function has some default processing capabilities: if no callback function is specified and if an element is assigned to the ContentLoader, then the Ajax response is pasted either into the value property if that exists (for HTML Form elements like INPUT and TEXTAREA), otherwise the response is pasted into the innerHTML property of the specified element.

This modified version of ContentLoader can deal with Cross Domain requests (see next section) that require a proxy to handle the request from the server side. See the function postLoadSingleResource() for details.

var postLoadResources = new Array();
var amis =new Object();

amis.STATE_UNINITIALIZED=0;
amis.STATE_LOADING=1;
amis.STATE_LOADED_AND_PROCESSING=3;
amis.STATE_PROCESSED=4;
amis.STATE_LOADED_AND_WAITING=5;

    function el(id) {
      return document.getElementById(id);
    }


/*--- content loader object for cross-browser requests ---*/
amis.PostLoadResource=function           
                   ( elementId
                   , url
                   , requireProxy // boolean indicating whether or not the resource must be acquired through a proxy from a remote domain
                   , label
                   , processor // function reference of function that will process the resource when received
                   , refreshTime // time interval in seconds after which the resource should be reloaded and reprocessed
                   ) {
  this.elementId=elementId;
  this.url=url;
  this.requireProxy = requireProxy;
  this.state = amis.STATE_UNINITIALIZED;
  this.label = label;
  this.req = null;
  this.id = null;
  this.processor= processor;
  this.refreshTime = refreshTime;
}

  // create a new PLR object and add it to the array of PLR objects to be dealt with when the page has loaded
  function addPostLoadResource
           ( elementId
           , url
           , requireProxy // boolean indicating whether or not the resource must be acquired through a proxy from a remote domain
           , label
           , processor // function reference of function that will process the resource when received
           , refreshTime // in seconds
           ) {
     var plr = new amis.PostLoadResource(elementId, url, requireProxy,label, processor, refreshTime);
     var size = postLoadResources.push( plr ); // add a new PostLoadResource object to the array
     plr.id = size -1; // ensure the plr objects knows where it sits in the postLoadResources array
     return plr;
  }


  function postLoadSingleResource(postLoadResource) {
      postLoadResource.state = amis.STATE_LOADING;
      if (postLoadResource.requireProxy) {
        loader = new net.ContentLoader('proxy?remotehost='+postLoadResource.url, processPostLoad);
      }
      else {
        loader = new net.ContentLoader(postLoadResource.url, processPostLoad);
      }
      loader.parameter = postLoadResource;
  }// postLoadSingleResource


  function goPostLoadResources() {
    // iterate array postLoadResources
    for (i=0;i<postLoadResources.length;i++) {
      if (postLoadResources[i].state == amis.STATE_UNINITIALIZED) {
        postLoadSingleResource(postLoadResources[i]);
      }
    }// for
  }// goPostLoadResources

  // This function is called by the ContentLoader when the AjaxRequest is successfully completed
  // and the requested content is available for further processing.
  function processPostLoad() {
    var postLoadResource = this.parameter;
    postLoadResource.req = this.req;
    postLoadResource.state = amis.STATE_LOADED_AND_PROCESSING;
    startProcessing( postLoadResource);
}// processPostLoad

  function handleTimeOut(id) {
    postLoadSingleResource(postLoadResources[id]);
  }

  // This function is called when a postLoadResources is loaded
  // and the requested content is available for further processing.
  // There is a number of things we can do in terms of post-processing
  // * use the contents as is
  // * xslt transform the contents
  // * HTML scrape the contents
  // * split CSV or otherwise pre-process the results
  // taking the result of this first step, we can:
  // - paste into the innerHTML of a specified element, for example a DIV or TD
  // - copy into the value attribute of a Form element like a TEXTAREA or INPUT
  // - process into <OPTION> elements under a select
  // - copy into JavaScript memorystructure
  // - call custom processor to anything special like populate SVG object
  function startProcessing(postLoadResource) {  
    if (postLoadResource.processor) 
    { 
      postLoadResource.processor.call(postLoadResource);
      }
    else {
      if (postLoadResource.elementId) {
        // go find element and load contents
        var element = el(postLoadResource.elementId);
        if (element.value) {
          element.value = postLoadResource.req.responseText;
        }
        else {
          element.innerHTML = postLoadResource.req.responseText;
        }
      }
    }// processor
    postLoadResource.state = amis.STATE_PROCESSED;
    if (postLoadResource.refreshTime) {
      setTimeout("handleTimeOut('"+postLoadResource.id+"')", postLoadResource.refreshTime*1000);
    }
  }// startProcessing

Using this library, we can create repeating Ajax requests as follows:

  <script type="text/javascript">
  function startRequest() {
    addPostLoadResource
    ( "postreads"
    , "https://technology.amis.nl/blog/postreads.php"
    , false // no proxy (note: this will run only in IE for a trusted domain)
    , "Get AMIS Blog Total Postreads"
    , null // no function reference needed: default handling will paste content in postreads div
    , 5 //refreshTime in seconds
    );
    goPostLoadResources();
  }
  </script>

Note: the above script gives problems in IE – though it should not. If I first load the page, it runs okay. When I refresh, it complains about objects to not having been defined. If I empty the browser cache, refresh the page and run again, it is fine again, until the next time I refresh. For Firefox is it running as it should – though not cross browser unless I use the proxy.

Cross Domain Requests

Browsers try to be quite serious about security. One of the rules most browsers enforce is that a document loaded in the browser cannot make Ajax requests to any other website (domain) than the one the document was loaded from. So if our document loads from our web server, we cannot make an Ajax request to an RSS feed or Web Service just anywhere on the internet. That can be a pity – even though this security thing is of course for our own good.

To deal with this, there are two main routes: one is try to workaround the restrictions in the browsers, using tricks or hacks that are far fetched, not secure or do not feel right. In Firefox for example there is very way to manipulate the security system; however, it is browser specific and does not appeal to me.  The other one is to use a proxy: a generic server side component – can be a servlet, a PHP script or anything really – that receives our requests and turns it into a request of its own to the URL we wanted to access in the first place. When the server side receives the reponse from the remote url, it will simply pass back the content to the browser. So indirectly, we can get content from any remote web site in our Ajax requests. For more details, see the article:Proxy Servlet for AJAX requests to multiple, remote servers

For the purpose of this workshop, we took the easy way and just used Internet Explorer and the work around described below. Therefore, we did not need a proxy servlet to make the remote calls for us.

Working round the cross domain browser issue in Internet Explorer

It is turns out to be deceptively simple to deal with the issue of cross domain requests in Internet Explorer: if you tell IE that the domain that you loaded the base document from – that is: the address or URL you can see in the location-bar of the browser, or at least the first part of it – is a trusted site, you can make all sorts of cross domain (Ajax) requests from JavaScript! To define a domain as a trusted site, go to Tools, Internet Options, pick Security (second tab) and go to Trusted Sites. Click on Sites and type the domain (http://the.domain.com or http://the.domain.com:port) in the Add this Web site to the zone field. Then click Add and presto: you can make Ajax requests from any document you load from the now trusted site to any site anywhere on the internet!

Resources

We briefly discussed a number of libraries and frameworks that may aid in doing Ajax style development. The most prominent ones discussed were:

We also discussed the upcoming changes in JSF 1.2 that hopefully will make integration of Ajax concepts with JSF somewhat easier (possibly allow a subset of components in a view tree to render their response and possible allow for a special non-visual render mode in which components render some sort of structured response (XML?), geared towards contents and not layout/markup).

Books on AJAX

Two very useful books, that we used in preparation for this workshop. The first one is somewhat more fundamental and theoretical, discussing for example a substantial number of Design Patterns and approaching Ajax in quite serious way. This book introduced the basic JavaScript library we have used in the demonstration above. The second book will get you started much sooner. It does not go into very complex and advanced stuff, but to get the hang of Ajax and know where to find more info, I would say it is more than adequate.

Ajax in Action, Dave Crane and Eric Pascarello with Darren James, Manning, October, 2005 | 680 pages, ISBN: 1932394613

Foundations of Ajax, by Ryan Asleson, Nathaniel T. Schutta; APress, October 2005, 296 pages; ISBN: 1-59059-582-3

4 Comments

  1. Gary Hepting July 3, 2007
  2. Lucas Jellema April 3, 2006
  3. Marco Gralike April 3, 2006
  4. Lucas Jellema April 3, 2006