Working with Ext.js

From BriansWiki

Jump to: navigation, search

Contents

[edit] Next Generation Application Development

My introduction to Ext began with a request from one of our departments for a "next generation" web application; they essentially wanted iGoogle, but made just for them. And why not? It makes good sense, and the design has proven to be effective. Since we are primarily a ColdFusion shop, these new requirements were going to be hard to fill; we had already been through our Flex phase and we found the black-box nature of Flex applications difficult to debug when we hit a technical wall. We are also being challenged by other divisions who are developing sites that use the Microsoft SharePoint portal; it is a rich interface but we don't want to sell our souls to Redmond for the visual bells a whistles of this proprietary platform.

That left me with the development environment what we currently liked best; Coldfusion, Javascript and its related AJAX libraries.

[edit] AJAX and the "Trough of Disillusionment,"

We have used AJAX in almost all of our new applications and we are generally happy with the results, we have had some problems with connectivity challenged clients however; the "chatty" nature of the architecture means more points of failure. We are also being asked to provide some sort of disconnected client data collection tools that will allow a single application to provide a seamless environment for our users. All of which leaves me to believe that we are reaching the technical limitations of AJAX.

New Technology Progrssion

[edit] Application Requirements:

Here's what we need to provide:

  • RIA clients for the desktop
  • A consistent user-experience across desktops
  • Allow for off-line and on-line interactions
  • Store and process large data sets stored on the desktop
  • Anne Thomas Manes of the Burton Group has called these apps "Fit Clients"

[edit] FIT Clients

There are several current examples of fit clients:

  • Adobe Air
  • Java Web Start
  • Google Gears
  • SlingShot (Java Based)

[edit] Intro to Ext 2.0

Since we like Javascript best and being a ColdFusion shop, we are naturally drawn to AIR. We were also intrigued to see that Adobe has incorporated a number of features of the Ext javascript library into CF8. Digging into their web site revealed the following impressive features:

  • Adobe Air support
  • Commercial license available
  • Phone support
  • Excellent API documentation and examples
  • Rich client interface
  • Integration with XML and JSON data sources

[edit] Client side vs. Server Side Frameworks

A little more Googling seemed to reveal two approaches to integrating with Ext and the other Javascript libraries; Extending the ColdFusion tags and using pure Javascript UI's with ColdFusion on the backend. I have to confess that I think using tags for programming is clunky and inefficient in execution. I always prefer to use scripts if I can get away with it so I was leaning towards the client side Javascript approach; here the breakdown of the pros and cons:

[edit] Pros

  • Full Ext library not just a few components
  • Object Oriented features
  • Flexible Client-side deployment – can use Air
  • True Rich Client UI (Fit client)
  • Better integration options – use ASP, CF or PHP data sources

[edit] Cons

  • Larger initial download
  • Not tag based - need more in depth JS knowledge

[edit] 2) Consuming XML data using the Ext.data.XmlReader

With the "Pros" looking pretty decisive, I started to look for ways to integrate ColdFusion data with Ext based clients, the first approach was to use XML since I had experience generating it from my Flex projects.

[edit] Why XML?

  • Readable data
  • You don’t have control over the source
  • Client needs XML

[edit] Simple XML Conversion:

The simplest way to create XML is to use the cfXml tag and an output loop like this:

<cffunction name="getUserListForGrid" access="remote" output="false" returntype="xml" hint="Returns all the users in xml format.">  
	<cfset var qGetUsersForGrid = "">  
	<cfset var qUserList = getUserList()>
    
	<cfquery name="qGetUsersForGrid" dbtype="query">  
        	SELECT userID, userfullname, email, deptname
		FROM qUserList
	</cfquery>
		
	<!--- Set the content type so ext can read it --->
	<cfcontent type="text/xml">
		
	<!--- Create the XML header and then output the query --->
	<cfxml variable="xmlData">
	<DATASET>
		<ROWCOUNT><cfoutput>#qGetUsersForGrid.recordcount#</cfoutput></ROWCOUNT><cfoutput query="qGetUsersForGrid">
		<ROW>
			<USERID>#userid#</USERID>
			<USERFULLNAME>#userfullname#</USERFULLNAME>
			<DEPTNAME>#deptname#</DEPTNAME>
			<EMAIL>#email#</EMAIL>
	   	</ROW></cfoutput>
	</DATASET>
	</cfxml>	

	<cfreturn xmlData>
</cffunction>

[edit] Abstract XML Conversion:

A more abstract approach uses introspection to build the XML dynamically:

<!---use Ray Camden’s toXML CFC to convert the query to an XML file --->
<cfset toXML = createObject("component", "toXML")>
		
<!---use the queryToXML function to convert the query results to xml text.  dataset will be the name	for the root node and row will be the node that repeats for each row in the query result--->
<cfset callXML = toXML.queryToXML(qHistory, "dataset", "row")>
		
<!---convert the XML text into an XMLDocument object since we are returning XML--->
<cfset parsedCallXML = xmlParse( callXML ) >
	
<!---Set the content returned so that Spry will recognize it as XML.  
NOTE: This command is not needed for Flex 2.0 --->
<cfcontent type="application/xml; charset=UTF-8">
<cfreturn  parsedCallXML />

[edit] 3) Consuming JSON data with the CFJsonReader

A more efficient way to use ColdFusion data is via the JSON standard; this is essentially XML with a bare minimum of markup:

[edit] Why JSON

  • Compact (but not easily readable)
  • Client may require it

[edit] Conversion Tools

Since this seems like a reasonable approach, we went looking for a way to convert a query into the JSON format; Here are the approaches I found:

[edit] CFJsonReader

Unfortunately I was not able to get any of these cfc converters to work. I ultimately settled on an approach that changes the way Ext processes the CF8 derived JSON data, figuring that this leads to a simpler and faster processing on the server side.

We just use the CF8 QueryConvertForGrid()function and reference the cfc with a "?returnFormat=JSON" argument. You could also compound the serializeJson and QueryConvertForGrid functions like this:

serializeJson(QueryConvertForGrid(qUsers, 1, qUsers.recordcount))
and leave off the url parameters on the client side but I think the former is more elegant and flexible. You can get the CFColdFusion.js here:

http://extjs.com/forum/attachment.php?attachmentid=3528&d=1198775119

This returns JSON that looks like this:

{"TOTALROWCOUNT":9,"QUERY":
		{"COLUMNS":
		["USERID","USERFULLNAME","EMAIL","DEPTNAME"],
	"DATA":[
		["bbrown","Bill Brown","user@test.com","IT"],
		["cstrahm","Chris Strahm","user@test.com","Operations"],
		["guest1","Test User","user@test.com","Test"],
		["guest2","Test User 2","user@test.com","Operations"],
		["guest3","Test User 3","user@test.com","IT"],
		["guest4","Test User 4","user@test.com","Operations"],
		["lsampson","Linda Sampson","user@test.com","Operations"],
		["rhansen","Rober Hansen","user@test.com","Operations"],
		["rwilliams","Bob Williams","user@test.com","Operations"]
		]
	}
}

Note: I added carriage returns for readability. Here are all the other outputs for comparison:

[edit] SerializeJSON() output: (without the QueryConvertForGrid function)

{"COLUMNS":
	["USERID","USERFULLNAME","EMAIL","DEPTNAME"],
	"DATA":[
	["bbrown","Bill Brown","user@test.com","IT"],
	["cstrahm","Chris Strahm","user@test.com","Operations"],
	["guest1","Test User","user@test.com","Test"],
	["guest2","Test User 2","user@test.com","Operations"],
	["guest3","Test User 3","user@test.com","IT"],
	["guest4","Test User 4","user@test.com","Operations"],
	["lsampson","Linda Sampson","user@test.com","Operations"],
	["rhansen","Rober Hansen","user@test.com","Operations"],
	["rwilliams","Bob Williams","user@test.com","Operations"]
	]
}

[edit] Json.cfc output:

{"recordcount":9,
	"columnlist":"deptname,email,userfullname,userid"
	"data":{
	"deptname":["IT","Operations","Test","Operations","IT","Operations","Operations","Operations","Operations"],
	"email":["user@test.com","user@test.com","user@test.com","user@test.com","user@test.com","user@test.com","user@test.com","user@test.com","user@test.com"],
	"userfullname":["Bill Brown","Chris Strahm","Test User","Test User 2","Test User 3","Test User 4","Linda Sampson","Rober Hansen","Bob Williams"],
	"userid":["bbrown","cstrahm","guest1","guest2","guest3","guest4","lsampson","rhansen","rwilliams"]}}

[edit] ToJson.cfc output:

{"data":[
	{"DEPTNAME":"IT","EMAIL":"user@test.com","USERFULLNAME":"Bill Brown","USERID":"bbrown"},
	{"DEPTNAME":"Operations","EMAIL":"user@test.com","USERFULLNAME":"Chris Strahm","USERID":"cstrahm"},
	{"DEPTNAME":"Test","EMAIL":"user@test.com","USERFULLNAME":"Test User","USERID":"guest1"},
	{"DEPTNAME":"Operations","EMAIL":"user@test.com","USERFULLNAME":"Test User 2","USERID":"guest2"},
	{"DEPTNAME":"IT","EMAIL":"user@test.com","USERFULLNAME":"Test User 3","USERID":"guest3"},
	{"DEPTNAME":"Operations","EMAIL":"user@test.com","USERFULLNAME":"Test User 4","USERID":"guest4"},
	{"DEPTNAME":"Operations","EMAIL":"user@test.com","USERFULLNAME":"Linda Sampson","USERID":"lsampson"},
	{"DEPTNAME":"Operations","EMAIL":"user@test.com","USERFULLNAME":"Rober Hansen","USERID":"rhansen"},
	{"DEPTNAME":"Operations","EMAIL":"user@test.com","USERFULLNAME":"Bob Williams","USERID":"rwilliams"}
]
}

[edit] SampleGrid.js

Putting all this together, you could create a full featured grid with a simple javascript function like this:

SampleGrid = function(limitColumns){

    var columns = [
    	{header: "ID", width: 50, sortable: true, dataIndex: ‘USERID’},
	{header: ‘Dept’, width: 90, sortable: true, dataIndex: ‘DEPTNAME’},
	{header: ‘Email’, width: 90, sortable: true, dataIndex: ‘EMAIL’},
	{header: ‘Name’, width: 90, sortable: true, dataIndex: ‘USERFULLNAME’
    ];

    // allow samples to limit columns
    if(limitColumns){
        var cs = [];
        for(var i = 0, len = limitColumns.length; i < len; i++){
            cs.push(columns[limitColumns[i]]);
        }
        columns = cs;
    }
	
   
    // create the data model
	var myDataModel = [
                // set up the fields mapping into the json
                {name: ‘USERID’, type:’string’},
                {name: ‘USERFULLNAME’, type:’string’},
                {name: ‘EMAIL’, type:’string’},
                {name: ‘DEPTNAME’, type:’string’}
                ]
                
	// create the reader
	var myReader = new Ext.data.CFJsonReader(myDataModel,{id:’USERID’})
	
    	var myStore = new Ext.data.Store({
	proxy: new Ext.data.HttpProxy({
	url: 'cfc/user.cfc?method=getUserListForGrid&returnFormat=json'}),
       	reader: myReader
    });

    myStore.load();
    
    SampleGrid.superclass.constructor.call(this, {
	store: myStore,
       	columns: columns,
       	autoExpandColumn: ‘Dept’,
       	viewConfig: {
		forceFit: true
	},
	renderTo: ‘content’,
	width: 500,
	frame: false,
	autoHeight: true
    });
}

Ext.extend(SampleGrid, Ext.grid.GridPanel);

Obviously this is much more verbose that a tag, but if you use cfTemplate to generate the code, you can speed up the process enormously.

[edit] Supporting html

Here's the simple html that calls the Scripts:

<html>
<head>
    <title>CF Json Test</title>
    
    <!-- Include Ext and app-specific scripts: -->
    <script type="text/javascript" src="scripts/extjs/adapter/ext/ext-base.js"></script>
    <script type="text/javascript" src="scripts/extjs/ext-all-debug.js"></script>
	<script type="text/javascript" src="cfJson.js"></script> 
	<script type="text/javascript" src="scripts/cfJsonreader.js"></script> 
	
	
    <!-- Include Ext stylesheets here: -->
    <link rel="stylesheet" type="text/css" href="scripts/extjs/resources/css/ext-all.css">
</head>
<body>
	<h1>TWCNY CF Json Test</h1>

    <div id="content"></div>
</body>
</html>

[edit] Links

Working with Ext Presentation (pdf)

Ext API Documentation

Ext.js Download

Personal tools