Ajax - Data Point Architecture
Posted: Monday, March 13, 2006
by Shabbir Hussain
Ajax - Data Point Architecture
Introduction
In past couple of days I was looking into various pattern and framework design around Ajax and one or the other is trying to solve the puzzle of runtime substitution of data and component within the page already rendered on the browser. But what if u need to have a true windows feel on the browser without using Flash. The answer is in Asynchronous JavaScript with a framework capable of substituting data points within view with actual data.
The architecture is based on a simple concept that the view consists of two parts: one is the component and other data. The web page is a combination of browser component and actual data in that component. If we create the web page providing the component with unique id then the value can be assigned as late as in browser (i.e. client) or as early as in web container (i.e. server). We will concentrate on substituting the component value in client (i.e. browser) to give it a windows feel.
Architecture
.jpg)
Figure 1.1
How it Works
1) It all begins on body onLoad event. The init method is called and it loads the first page into the View Cache then it calls getViews method.
2) The method first checks if the configuration file (a.k.a webflow.xml) is downloaded and processed from the server. If not then it download the file and parse it.
3) From the flow section of this file it finds out what are the targets from first page and then tries to retrieve all the view for the targets. After this operation what the client have is all the views that can be navigated from first page
4) When the user clicks on the link or submit a form then it calls renderPage that tries to get the data and substitute the data at the data points in the view.
The step 3 & 4 is repeated for every page to get the target views and using renderPage to get the data.
Example
Lets start to implement the concept presented above to make our hands dirty. I am going to present a very simple application so that u guys can follow what I am saying
Following are the files used in this example.jpg)
Figure 1.2
The flow of the application is defined in webflow.xml. In our example from index.html the user can navigate to linkexample.html or to formexample.html. It is represented in webflow.xml
<webflow>
<page name="index">
<targetview name="linkexample" link="http://localhost:8085/ajax/view/linkexample.html"/>
<targetview name="formexample" link="http://localhost:8085/ajax/view/formexample.html"/>
</page>
</webflow>
The link property points to view file.
Next we will look inside the index.html. As I said previously it all begin with onLoad event.
The javascript code for asynchronous communication and xml parsing in inside ajax.js. Lets start with a look at init method
function init(pageName)
{
var idiv = window.document.getElementById("page")
viewCache.put(pageName, idiv.innerHTML)
getViews(pageName)
}
viewCache is an object of Hashtable. It cache all the views. As the index.html is the first view it also need to be loaded into cache.
getViews perform 2 main operations. First it checks if the webflow.xml is downloaded or not. If not then it gets the webflow.xml and loads that into config object. Second using the information from webflow.xml it determine all the target views that need to be download for current page
function getViews(pageName)
{
if (window.XMLHttpRequest) {
req = new XMLHttpRequest()
} else if (window.ActiveXObject) {
req = new ActiveXObject("Microsoft.XMLHTTP")
}
if (config.size() == 0) { getConfig(pageName) return}
var targetViewTable = config.get(pageName)
if (targetViewTable.size() > 0) {
viewName = targetViewTable.keys()[0]
url = targetViewTable.values()[0]
req.open("GET", url, true)
req.onreadystatechange = function(){processView(pageName, viewName, 0)}
req.send(null)
}
}
getConfig does the first part and the remaining code perform the second part
function getConfig(pageName)
{
// Using XMLHttpRequest get the webflow xml
// Using the webflow xml start getting the view till the level specify by depth and
// load that into cache
if (window.XMLHttpRequest) {
req = new XMLHttpRequest()
} else if (window.ActiveXObject) {
req = new ActiveXObject("Microsoft.XMLHTTP")
}
req.open("GET", configURL, true)
req.onreadystatechange = function(){processConfig(pageName)}
req.send(null)
}
processView and processConfig are callback method. It gets data and after parsing the data it loads it into respective cache.
Now lets look into later part of index.html. In this method we pass the view name, data url, post value and merge flag. View name and data url is self-explanatory. What is post value? As we are using the XMLHttp to post the data we have to form the post value in the form of name-value pair just like GET query string. The merge flag tells if the data need to merge with the existing data. It can be used in case of validation form Next we are going to look at the processData function processData(viewName) { function replacePage(viewName, data) function parseDataXML(viewName, txt) var xmlDoc = new ActiveXObject("Microsoft.XMLDOM") dataNode = xmlDoc.getElementsByTagName("data")[0] dataPointTagsCount = dataNode.childNodes.length for (i=0i return newPage These methods are also self-explanatory. dataCache holds the data for a view and replacePage parses the data and replace the view point with actual data. Finally the innnerHTML of the div tag (window) is replace with new view (page). Showing linkexample.html (view) and linkexample.jsp (data) below linkexample.html linkexample.jsp The entire source code for the example can be downloaded at http://www.geocities.com/shabbirkoth/ajax/ajax.zip The architecture presented in this article is in infancy stage. There are lots of thing that can be done with this as a starting point. Following are the highlights that are missing in the architecture presented If we have all features on top of above architecture then we can have a true framework (and I will not hesitate to call it as a framework).
<DIV id="page">
<TABLE>
<TR>
<TD>Link Submit</TD>
<TD>Form Submit</TD>
</TR>
<TR>
<TD><a href="#" onClick="renderPage('linkexample',
'http://localhost:8085/ajax/data/linkexample.jsp?id=1', null, false) return false">1
</TD>
<TD>
<form name="myform">
<input type=text name="id" size="10"><br>
<input type="button" value="Submit" onClick="renderPage('formexample',
'http://localhost:8085/ajax/data/formexample.jsp', getPostValue(document.myform), false)">
</form>
</TD>
</TR>
</DIV>
This code contain two target one is link and another is form submit. Both of this uses renderPage to get the data. Now lets look into renderPage code
function renderPage(viewName, dataURL, postValue, merge)
{
//Check if view is available in view cache
if (viewCache.containsKey(viewName))
{
if (merge) {
//Check if data exist. If u point to the same page with new data
//Merge data
}
else {
//Get the Data
if (window.XMLHttpRequest) {
req = new XMLHttpRequest()
} else if (window.ActiveXObject) {
req = new ActiveXObject("Microsoft.XMLHTTP")
}
if (postValue == null) {
req.open("GET", dataURL, true)
req.onreadystatechange = function(){processData(viewName)}
req.send(null)
} else {
req.open("POST", dataURL, true)
req.setRequestHeader('Content-Type','application/x-www-form-urlencoded')
req.onreadystatechange = function(){processData(viewName)}
req.send(postValue)
}
}
} else {
//Get the view and load it into cache
//Get the Data
}
}
if (req.readyState == 4) {
if (req.status == 200) {
var data = req.responseText
dataCache.put(viewName, data)
replacePage(viewName, data)
}
}
}
{
//Parse the data and replace data point with actual data
newPage = parseDataXML(viewName, data)
var idiv = window.document.getElementById("page")
idiv.innerHTML = ""
idiv.innerHTML = newPage
}
{
var newPage = viewCache.get(viewName)
xmlDoc.loadXML(txt)
tagName = dataPointNode.nodeName
tagValue = dataPointNode.text
tagName = "$" + tagName
newPage = newPage.replace(tagName, tagValue)
}
}
Click on = $id
<data>
<id><%=request.getParameter("id")%></id>
</data>
Summary