ExtJS 3.0 Grid with ColdFusion 8/ColdFusion 9

With the recent release of ExtJS 3.0, I have been looking at the library again. I decided to do a simple Grid implementation using ExtJS. ExtJS is probably one of the best looking JS libraries out there out of the box and when implemented its extremely quick. The main issue as has been when integrading jQuery with ColdFusion was passing the JSON back in the proper format for the grid.

Have a look at the Demo First Here.

And now lets look at the code.

The HTML is very simple. We include the Ext libraries and also the paging.js file which contains out Javascript for the Grid.
   view plainprintabout
 <html>    
     <head>
         <title>Ext JS Grid with ColdFusion</title>
         <!--- Get all needed ext libraries files --->
         <link rel="stylesheet" type="text/css" href="ext/resources/css/ext-all.css">
         <script src="ext/adapter/ext/ext-base.js"></script>
         <script src="ext/ext-all-debug.js"></script>        
         
         <script src="paging.js"></script>
10          
11      </head>
12      <body>
13          <!--- This will be out grid div --->
14          <div id="topic-grid"></div>
15          
16      </body>
17  </html>
Now lets look at the javascript.
   view plainprintabout
 /*!
  * Ext JS Library 3.0.0
  * Copyright(c) 2006-2009 Ext JS, LLC
  * licensing@extjs.com
  * http://www.extjs.com/license
  */

 Ext.onReady(function(){
     
     //This function will be called on a succesful load, it can be used for debugging or perform on load events.
10      function testStore(st,recs,opts){        
11   //console.info('Store count = ', store.getCount());     
12      }
13      
14      //This is our JSON record set which defines what kind of data will be present in the JSON passed back from our component.
15      var users = Ext.data.Record.create([
16      {name:'ID',type:'int'},
17      {name:'FIRSTNAME',type:'string'},
18      {name:'LASTNAME',type:'string'},
19      {name:'DISPLAYNAME',type:'string'},
20      {name:'USERNAME',type:'string'},
21      {name:'USERACCOUNTINGCODE',type:'string'},
22      {name:'PHONE',type:'string'}
23      ])
24      
25   // create the Data Store
26   var store = new Ext.data.JsonStore({
27       totalProperty:'DATASET',//This is how many total records are there in the set.
28       root:'ROWS',//The Root of the data.
29   url:'http://coldfusion-ria.com/Blog/KSPersonal/getStuff.cfc',//Where we get it from
30   remoteSort:true,//We will sort server side
31   //Base Params are parameters passed in during the first call
32   baseParams:{
33       method: 'getStuffA',
34       returnFormat: 'JSON',
35       start: '0',
36       limit: '50'
37   },
38   //We define the JSON Reader for the data. We also need to set the totalProperty, root and idProperty for the dataset here.
39   reader: new Ext.data.JsonReader({
40           totalProperty:'DATASET',
41           root:'ROWS',
42           idProperty:'ID'     
43       },users
44   ),
45   //Fields read in
46   fields: [
47   'ID','FIRSTNAME','LASTNAME','DISPLAYNAME','USERNAME','USERACCOUNTINGCODE','PHONE'
48   ],
49   //We specify the listeners to be called during load or another one during loadexception. good for debugging purposes.
50   listeners: {
51   load:{
52       fn: testStore
53   },
54   loadexception: {
55       fn: function() {
56       //console.log(arguments);
57       //console.info("Response Text?"+response.responseText);
58       //console.log("dgStore Message \n"+proxy+"\n"+store+"\n"+response+"\n"+e.message);
59       }
60   }
61   }
62   });
63   //We setup the Grid
64       var grid = new Ext.grid.GridPanel({
65   width:750,
66   height:500,
67   title:'Users',
68   store: store,
69   trackMouseOver:true,
70   disableSelection:false,
71   loadMask: true,
72          stripRows: true,
73          collapsible: true,
74   // grid columns
75   columns:[
76   new Ext.grid.RowNumberer(),//This will do numbering on the grid for us
77   {
78   id: 'users',
79   header: "First Name",
80   dataIndex: 'FIRSTNAME',
81   width: 125,
82   hidden:false,
83   sortable: true
84   },{
85   header: "Last Name",
86   dataIndex: 'LASTNAME',
87   width: 125,
88   hidden: false,
89   sortable: true
90   },{
91   header: "Display Name",
92   dataIndex: 'DISPLAYNAME',
93   width: 200,
94   hidden: false,
95   sortable: true
96   },{
97   header: "User Name",
98   dataIndex: 'USERNAME',
99   width: 125,
100   hidden: false,
101   sortable: true
102   },{
103   header: "Contact",
104   dataIndex: 'PHONE',
105   width: 100,
106   hidden: false,
107   sortable: true
108   }],
109  
110   // paging bar on the bottom
111   bbar: new Ext.PagingToolbar({
112   pageSize: 50,
113   store: store,
114   displayInfo: true,
115   displayMsg: 'Displaying Records {0} - {1} of {2}',
116   emptyMsg: "No Records to display"
117   })
118   });
119      
120  //Default Sort set for the grid load call
121  store.setDefaultSort('FIRSTNAME','ASC');
122  // render it
123  grid.render('topic-grid');
124  
125  // trigger the data store load
126  store.load();
127  });
The comments explain what I am mainly doing in the script. The basic process is as follows:
  1. Create the Data Record that will be the specification for the JSON Reader
  2. Create the JSON Store and point to the JSON Reader to our record we created
  3. Create the Grid, point the store to the JSON Store we created
  4. Create the Paging Toolbar for paging implementation
Next, lets look at our CFC.
   view plainprintabout
 <cfcomponent output="false">
     
     <cffunction name="getStuffA" access="remote" returnFormat="json" output="false">
         <cfargument name="limit" default="50">
         <cfargument name="start" default="1">
         <cfargument name="sort" default="FirstName">
         <cfargument name="dir" default="ASC">
         
         <cfset var arrUsers = ArrayNew(1)>
10          
11          <!--- ExtJS starts the dataset at 0, we increment it by 1 to get proper start and end rows --->
12          <cfset Arguments.start = Arguments.start + 1>        
13          
14          <cfquery name="selUsers" datasource="RIADemo">
15                  SELECT
16                      ID, FirstName, LastName, DisplayName, UserName, UserAccountingCode, Phone
17                  FROM
18                      Users
19                  ORDER BY #Arguments.sort# #Arguments.dir#
20          </cfquery>
21          
22          <!--- For the Array --->    
23          <cfset i = 1>
24          
25          <!--- Get the end record --->    
26          <cfset end = ((Arguments.start) + arguments.limit)-1>
27          
28          <!--- We look through the query and create our JSON return set. The ExtJS JSON return has to be in the format like:
29          {
30           results: 2, // Reader's configured totalProperty
31           rows: [ // Reader's configured root
32       { id: 1, firstname: 'Bill', occupation: 'Gardener' }, // a row object
33       { id: 2, firstname: 'Ben' , occupation: 'Horticulturalist' } // another row object
34           ]
35          }
36          This means that the data is returned as an array of objects (in our case structures). So we build that array.
37          --->

38          <cfloop query="selUsers" startrow="#Arguments.start#" endrow="#end#">        
39              <cfset stcUsers = StructNew()>
40              <cfset stcUsers.ID = #selUsers.ID#>
41              <cfset stcUsers.FIRSTNAME = #FirstName#>
42              <cfset stcUsers.LASTNAME = #LastName#>
43              <cfset stcUsers.DISPLAYNAME = #DisplayName#>
44              <cfset stcUsers.USERNAME = #UserName#>
45              <cfset stcUsers.USERACCOUNTINGCODE = #UserAccountingCode#>
46              <cfset stcUsers.PHONE = #Phone#>
47              <cfset arrUsers[i] = stcUsers>
48              <cfset i = i + 1>            
49          </cfloop>
50          
51          <!--- Final JSON return --->
52          <cfset stcReturn = {rows=arrUsers,dataset=#selUsers.RecordCount#}>
53          
54          <cfreturn stcReturn>
55      
56      </cffunction>
57  
58  </cfcomponent>
The main thing in the CFC is getting the proper JSON structure for the ExtJS Grid. This is easy when you understand that the grid data is expected to be in the format of an Array of Objects(Structures). Source Code: Download Update: This will also work with ColdFusion 9 and ExtJS 3.0

Related Blog Entries

Comments
Kevin Roche's Gravatar The example seems to take a long time to load and appears to load all rows at the start rather than a few at a time. What would it take to load them as needed rather than have the upfront delay?
# Posted By Kevin Roche | 7/13/09 12:34 PM
Kumar Shah's Gravatar @Kevin
One of the reason for that probably is that I am using the ext-debug files instead of the non-debug files. Also, the demo seems to be a lot faster for me in Internet Explorer 7/8 compared to FF.

I can assure you that not all rows are being loaded, because the CFC only returns 50 records at a time.
# Posted By Kumar Shah | 7/13/09 1:27 PM
Jeremy Martin's Gravatar I've also been having fun with CF8 and Ext also (2.0 and 3.0). Just so you know, you definitely don't HAVE to convert the query data into that format. If you send back the query as-is with returnFormat as "JSON", just set the reader's (in this case an ArrayReader's) "root" to your "DATA" portion of the query, and the "fields" of your reader to the "COLUMNS" portion of the query. If you need to, loop through those fields and configure them further. In my solution I'm configuring the reader fields by passing configuration data from Coldfusion and letting ExtJS loop through and apply whatever configuration I want. I have a post on the ExtJS forums describing this in more detail -- just search the forums for "Coldfusion 8" and "metadata".
# Posted By Jeremy Martin | 8/5/09 7:12 PM
Jerry's Gravatar This code works great, except for in IE6. The grid would never render to the page. Mind you I am in a closed network environment. Either way after reading some of the documentation I made one small change that seem to fix the issue for me. I set the reader as a variable instead of doing it in line. Like so..
var myReader new Ext.data.JsonReader({
totalProperty:'DATASET',
root:'ROWS',
idProperty:'ID'
},users
);
And then just used the variable myReader for the reader option. Like...
reader: myReader,
Also if you are working in a closed network environment IE6 will continue to make a call to get http://extjs.com/s.gif which caused my data to have huge latency until it finally stopped trying to make the call. Put this code before your grid function and it works like a charm.
Ext.BLANK_IMAGE_URL='include/images/blank.gif';
3 days and finally got it to work. Woooo!
# Posted By Jerry | 10/21/09 1:21 PM
Kumar Shah's Gravatar Thanks for the Information Jerry.
# Posted By Kumar Shah | 10/23/09 1:09 PM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.5.003.  Design based on ARCLITE by: digitalnature