Tuesday, June 16, 2009

AJAX and XMLHTTPREQUEST (IE -vs- FF)

I have discovered a good deal regarding the use of the ajax critical xmlhttprequest object. It is central to ajaxing. As the wiki pedia artical points out, it is how the client communicated with the server. The ajax frameworks that are abound, help to bridge the gap for developers who are not real strong in javascript. That would be me, but i am getting a real baptizing in javascript and the xmlhttprequest object.

Iwrested for about a day getting my existing ajax code that is using this object to work in both FF and IE. I implemented some ajax code in a utility about a 2 years ago, that never worked in IE. I was not too worried, since the utility is for admin users, a small audience, so i said "use only in FF". That was ok for initial success, but now that i am wanting to use ajax in another utility and probably in our main application, i knew it was time to get a better handle on some of this stuff.

I was digging into some frameworks and getting a little confused, this lead me into other jscript libraries and code, like JSON and JQuery - which i think will matter more when i am actually returning pure XML or WDDX formatted data and dealing with it in javascript. Anyway - i decided that i would solve first the code that was out there and get it to work across both browsers. This lead me into the issue of IE and FF having different implementations. IE tends to treat the W3C standards as recommendations.

Eventually i was able to solve the problem with IE, which was a caching problem. Once i added code like this;

function showQuestions(AID, flag){ xmlHttp = GetXmlHttpObject();
var url="views/filterQuestions.cfm?&assessmentID=" + AID + "&showAllQuestions=" + flag ; xmlHttp.open("POST", url , true); xmlHttp.onreadystatechange=updateQuestionList; xmlHttp.send(null);}

 Putting the open statement before the onreadystate call was the main reason it was caching.  Once i moved the statement, it worked for the first request, but was still caching after that (in IE).   I added a line to the end of the query string to solve this problem.

var url="assets/filterQuestions.cfm?&assessmentID=" + AID + "&showAllQuestions=" + flag + "?RandomKey=" + Math.random() * Date.parse(new Date());

I found this workaround posted here

The IE  problem was complicated because i was using the onClick event on the items in my select box, which was fine for FF, but was not for IE.  Eventually, using alerts() in the code, i was able to trace the problem to the select controls not firing in IE.

This is what the initial html looked like that worked in FF, but not IE.

<select name="subject"> <option value="ela" onclick="showAssessments(this.value)">ELA</option> <option value="math" onclick="showAssessments(this.value)">Math</option> <option value="science" onclick="showAssessments(this.value)">Science</option> <option value="socialStudies" onclick="showAssessments(this.value)">Social Studies</option> <option value="allSubjects" onclick="showAssessments(this.value)">All</option></select>

This is the corrected version.

<select name="subject" onchange="showAssessments(this.value)"> <option value="ela" >ELA</option> <option value="math" >Math</option> <option value="science" >Science</option> <option value="socialStudies" >Social</option> <option value="allSubjects" >All</option></select>

Once both of these issues where resolved, i was really about to focus on the xmlhttprequest code. This is the function i ended up using that i liked the best. It is cross broswer compatible and clean.

function GetXmlHttpObject () { var xmlHttp = null; try { // Firefox, Opera 8.0+, Safari xmlHttp = new XMLHttpRequest(); // alert('success'); } catch (e) { // Internet Explorer try { xmlHttp = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); } } return xmlHttp;}

A final AHA, for today.


By using ajax this way, storing the result of a file that is speaking to the db and doing the formatting and passing the result to a html element on a page, its effiecient, BUT, does not allow you to encapsulate db code into CFCs or other objects. I suppose you could, but you would not use a CFC and a DAO function that had html output in it. I

Friday, June 12, 2009

AJAX framework JSMX

While looking for commentaries on ajaxCFC, i ran across another ajax framework that was originally build for coldFusion, but is now being used by .net and php backends too.

JSMX

I like what im ready about this one, a very light footprint. There is only a single .js file to include in the page. That is really lite, and probably will do all that i need it to do at this point. Perhaps if i continue to push out ajax solutions, i will find a situation where a more *robust framework is needed.

I love this paragraph from the site;
"The beauty of this API is its simplicity and its straight forward syntax. It consists of just one file (engine.js) which marshals requests between the client and the server via the http() function (which is the only function you need to know about in order to use JSMX)."

and this too;

"You simply place the engine.js file in your display page. You then write two functions within JavaScript for each call to the server, a request function and a callback function. The request function makes either a "GET" or "POST" to the server by calling the http() function (which resides in the engine.js file). The server fires off the request and returns JavaScript, XML, JSON, or WDDX to engine.js which in turn converts the response into a valid JavaScript Object/Variable and returns it to the callback function in your display template."

The notion of a request and callback function both in the invoking method is common at all the frameworks that i have peaked at the past few days.

this framework will return to the invoking function (the view page) a javascript object, in either JSON, WDDX, XML form - depending on what you specify in the invoker.

Here is a sample implementation using this framework

1 - include the javascript file

<script src="JSMX/engine.js"></script>


2 - jscript in calling view page

<script>
function my_request(val){param = 'arg=' + val;
http( "POST" , "model/ajaxCalls.cfc?method=getPIKeys2" , getPIsHandler, param);
}
function getPIsHandler(r){ document.getElementById("showResults").innerHTML = sDumper(r);
}
</script>

This is the html code that calls the my_request() function
<form name="myForm">
<div>
<span>
<b>Select a pi key group:</b>
</span>
<span>
<select name="key" onChange="my_request(this.value)">

<option value="h">like h</option>
<option value="r">like r</option>
<option value="p">like p</option>
</select>
</span>
</div>

<div id="piList">
</div>
</form>



3 - coldfusion cfc function


<cffunction name="getPIKeys2" access="remote" returntype="query">
<cfargument name="arg" type="string" required="yes" default="z">

<cfquery name="q" datasource="accelerateu">
SELECT strandID, piID, auID, piKey, piDescription, piCode
FROM dbo_performanceIndicator

WHERE (piKey LIKE '%#arguments.arg#%')
</cfquery>


<cfreturn q>
</cffunction>

Here is the big thing that took me about 2 days to figure out!

I have been trying to pass arguments, parameters (whatever you wish to call them) to my coldfusion function, a very common practice/need. In each case, i was passing the value from the selector box to the jscript function as a paramater, then passing the parameter from the jscript function into the coldfusion function via the nice http object. But, the parameter was not being reconized in the CF function. I fiddled around and around with this, which actually caused me to look for other frameworks, since i could not get it to work in the ajaxCFC framework (althought it was not the problem of the framework). The problem was this.

In a javascript invocation, your parameter name MUST MATCH THE ARGUMENT NAME IN THE CFC. no kidding. If you pay attention to the code above, you notice the same name in the param statement and in the CFC function, arg .

That arguments/parameter could be called bob or joe or skippy, as long as the name is the same. I guess i get spoiled or lazy by using ColdFusion all these years, which does not care if the name is the same, just the order and data type.

So thats it. A pretty good accomplishment, i needed to slove this, so i can get on with manipulating the returned data set using javascript to loop and output, not CF.

and, oh yea, its cross browser compatible - it works in IE and FF and Safari. Its actually a little zippier in Safari.

peace out for now.

ajaxCFC coldfusion ajax framework

well, i like to blog, it helps me to grok stuff. I am trying out the ajaxCFC framework.

"AJAX is Asynchronous JavaScript and XML. It is not a technology, but an umbrella that combines several concepts to enrich user experience by allowing server interaction without refreshing the browser."

Got that part~!

There are some useful examples on Robs home page - and some good 3rd party review comments on the framework.

AJAX under the hood a bit more

In my second attempt to use ajax techniques in a new application, my first through was
1 - it needs to work in all major browsers
2 - i need to put my code that is speaking to the DB in the model, and call it as a function and deal with the results in the view page.

For me, a natural progression. In my first implementation, i did not really use javascript to render the result of my asychonous call - i just output the result of the view page to a html div. This was ok for initial success, but it needs to work across IE and FF and i would like to keep presentation code separated from server code and let javascript be the one who is rendering the result set.

This is a little more difficult. I am spoiled by cold fusion and its query construct, which makes it very easy to get a resultset and output it in a html page.

Initially, i did a search for cold fusion ajax and tutorials and found alot of stuff, including, no big surprise here, a AJAX framework for cold Fusion. Rob Gonda created ajaxCFC in 2006. There is also a framework called MxAJAX and CFAjax. I also found a google group for MXAjax. Needless to say, there is alot to get your head around.

Some of the things im realizing is that at some point, you have to bit the bullet (so to speak) and roll up your sleeves and get into javascript and how it works with queries and other complex data types (structs and arrays). This is really what we need to master to become proficient at ajaxing. The frameworks and tags and other helper apps, utils that are available are really masking the need to learn how javascript works natively on complex data types.

I found this comment that summed up this point well.

"At some point you're going to have to accept that there
is no client side ColdFusion. The "J" in AJAX is JavaScript. If you're
going to be working with AJAX, you're going to have to bite the bullet at
some point and roll up your sleeves and work with JavaScript. Or, yes, you
can use the built in AJAX functionality within CF. depending on what you
need to do it might be robust enough. But (IMO) the built in AJAX stuff,
while handy for quick one-offs, is not a replacement for really knowing and
understanding how to work with AJAX.
"

I am learning about JSON (java script object notation) and JQuery (a javascript library of functions), each seems to be an important concept to grok regarding ajaxing.

I am currently trying to use the ajaxCFC framework, since it works with components and is recommended by enough CFers. It has a light footprint (this is lingo that means its not a real big framework in terms of number of files). and requires the developer to include a couple javascript files and a coldfusion cfc, that is then extended by cfcs that are being called using the framework.

Look at the next post for specifics regarding ajaxCFC.

AJAX - with no framework

My initial implementation of ajax involved three files.
1 - a form - that called the javascript
2 - javascript file, that contained funtion called by the form and called the model component - that goes to the db.
3 - a model component (CFC, class etc) that speaks to and retrieves the data and sends back

No framework, or complex JSON stuff. The javascript is really the heavy hitter here, since it is invokes, does the invoking, and tells the html div element to update with the content from the db.

Here is some sample code that does this.

1 - form that calls the jscript function


<form>
<div>
<span>
<b>Select a pi key group:</b>
</span>
<span>
<select name="Country" onChange="getPIs(this.value)">

<option value="h">like h</option>
<option value="r">like r</option>
<option value="p">like p</option>
</select>
</span>
</div>
<div id="piList">
</div>
</form>


2 - the javascript function getPIs is called in step 1 in the onchange event for the selector. In the getPIs function below - a url variable is set that points to my cfc that contains the function that goes to the db. Then a GetHttpObject call is made and assigned to the xmlhttp variable. In the stateChanged function that is passed as an argument to the GetHttpObject function, there is a reference to a div layer that is in the view page that is calling the jscript. That is the connection or the place where the result of the cfc call will output.


function getPIs(str)
{
var url="model/ajaxCalls.cfc?Method=getPIKeys&pikey="+ str;
xmlhttp=GetHttpObject(stateChanged)
xmlhttp.open("GET", url , true)
xmlhttp.send(null)
}

function stateChanged(){
if (xmlhttp.readyState==4 || xmlhttp.readyState=="complete"){
document.getElementById("piList").innerHTML=xmlhttp.responseText
}
}

function GetHttpObject(handler){
try{
var oRequester = new XMLHttpRequest();
oRequester.onload=handler
oRequester.onerror=handler
return oRequester
}
catch (error){
try{
var oRequester = new ActiveXObject("Microsoft.XMLHTTP");
oRequester.onreadystatechange=handler
return oRequester
}
catch (error){
return false;
}
}
}

3 - here is the coldfusion cfc method invoked by the jscript call

<cffunction name="getPIKeys" access="remote" returntype="any">
<cfargument name="arg1" type="any" required="No" default="h">

<cfquery name="q" datasource="accelerateu">
SELECT strandID, piID, auID, piKey, piDescription, piCode
FROM dbo_performanceIndicator

WHERE (piKey LIKE '%#arguments.arg1#%')
</cfquery>

<cfsavecontent variable="bob">
<cfoutput query="q">
<b>#pikey#</b> - #piDescription#.
<br/>
</cfoutput>
</cfsavecontent>

<cfreturn bob>
</cffunction>


This example is not real pretty and only executes in FF, not IE. This example does demonstrate a simple AJAX solution. It was for me, a natural first step. That being said, let me share some of my next step learning experiences in the next post.

AJAX 101

I started ajaxing about 1 year ago, thats june 08. Since ajax techniques have become more common and accepted, it was time for me to get going with it. There are no shortages of blogs about ajax or conferences or opinions - so you have to set a specific goal regarding what it is you want to do. Let me explain a bit more.

AJAX is not a single piece of technology - its an idea, implemented mainly in javascript that uses a technique of making a request to the database without having to fully reload the page. There are lots of more eloquent explanations with nice pictures to explain this further, but what i am after, as a developer, is to make the user experience better for software that i am designing. A noble goal. If the application has lots of data and state in it, and as a good developer, you want to maintain that state, then reloading the entire page or event (if you use a MVC framework) is alot of extra work.

It is not uncommon in a interface, to request updated data from the DB - where only a small part of the interface needs to update - so, in some instances, its more user centric to update just that smaller part of the interface for the user.

There are always tradeoffs, and when you use an ajax call to update part of the interface, you are really, to the user, disabling the back button. Since you did not reload the page via a new HTTP request, then there is no new page to "load" in the browser memory.

Sometimes the ajax code is not cross browser compatible. For example, IE and FF do not interpret parts of the DOM (document object) the same. The javascript executes the same in any browser, but the way that we communicate with the elements in the html page differ. This was my initial experience using ajax. Working in FF, but not in IE.

More to follow...