Using jQuery based jqGrid with ColdFusion: Part 1

So, this weekend I started looking at jQuery. I have seen many people around the blogosphere use it and it is surprisingly lightweight, and doesn't require a lot of code writing. There is also another problem, the ColdFusion cfgrid implementation slows down as the number of records increase. So, specifically I looked at jqGrid plugin for jQuery to implement a datagrid.

I took my previous work on a simple User List grid and started the process of replicating it using jQuery. Thankfully, I was not the first one to think of something like this, so I had a head start thanks Hal Helms

So to start off, have a look at the Demo

So, first lets look at the CFM page, then we will take a look at the CFC. First step is to include all the CSS and JS files we need for this.

   view plainprintabout
 <link rel="stylesheet" type="text/css" media="screen" href="themes/basic/grid.css" />
 <link rel="stylesheet" type="text/css" media="screen" href="themes/jqModal.css" />    
 <script src="jquery.js" type="text/javascript"></script>
 <script src="jquery.jqGrid.js" type="text/javascript"></script>
 <script src="js/jqModal.js" type="text/javascript"></script>
 <script src="js/jqDnR.js" type="text/javascript"></script>
We also specify, a table (our grid), a div for the pager. There is also a search div in this code, I will be implementing the search feature for the grid at a later date.
   view plainprintabout
 <table id="list" class="scroll" cellpadding="0" cellspacing="0"></table>
 <div id="pager" class="scroll" style="text-align:center;"></div>
 <div id="mysearch"></div>
Now, lets look at the meat, the JS code on the page.
   view plainprintabout
 $(document).ready(function()
         {
             $("#list").jqGrid(
             {
                 url:'Users.cfc?method=getUsers', //CFC that will return the users
                 datatype: 'json', //We specify that the datatype we will be using will be JSON
                 colNames:['ID','FirstName','LastName', 'DisplayName','UserName','UserAccountingCode','Phone'], //Column Names
                 //The Column Model to define the data. Note you can make columns non sortable, specify width, alignment, etc.
                 colModel :[
10                      {name:'id',index:'id', width:50, sorttype:"int"},
11                      {name:'FirstName',index:'FirstName', width:150, sorttype:"string"},
12                      {name:'LastName',index:'LastName', width:150, align:"left",sorttype:"string"},
13                      {name:'DisplayName',index:'DisplayName', width:150, align:"left",sorttype:"string"},
14                      {name:'UserName',index:'UserName', width:150,align:"left",sorttype:"string"},
15                      {name:'UserAccountingCode',index:'UserAccountingCode', width:150, sortable:false},
16                      {name:'Phone',index:'Phone', width:150, sortable:false}
17                  ],
18                  pager: $('#pager'), //The div we have specified, tells jqGrid where to put the pager
19                  rowNum:4, //Number of records we want to show per page
20                  rowList:[4,8,12], //Row List, to allow user to select how many rows they want to see per page
21                  sortorder: "asc", //Default sort order
22                  sortname: "ID", //Default sort column
23                  viewrecords: true, //Shows the nice message on the pager
24                  imgpath: '/PLite/Test/themes/basic/images', //Image path for prev/next etc images
25                  caption: 'Users', //Grid Name
26                  height:'auto', //I like auto, so there is no blank space between. Using a fixed height can mean either a scrollbar or a blank space before the pager
27                  recordtext:'Total Records', //On the demo you will notice "7 Total Records" - The Total Reocrds text comes from here
28                  pgtext:'/',//You notice the 1/3, you can change the /. You can make it say 1 of 3
29                  editurl:"Users.cfc?method=getUsers",//Not used right now.
30                  toolbar:[true,"top"],//Shows the toolbar at the top. I will decide if I need to put anything in there later.
31                  //The JSON reader. This defines what the JSON data returned from the CFC should look like
32                  jsonReader: {
33                      root: "ROWS", //our data
34                      page: "PAGE", //current page
35                      total: "TOTAL", //total pages
36                      records:"RECORDS", //total records
37                      cell: "", //not used
38                      id: "0" //will default first column as ID
39                      }
40                  }
41              );            
42              
43          }
44      );
I have specified comments in the code, hopefully that explains enough. the jqGrid site also offers excellent documentation on the properties and data. The next step is to write the getUsers function, and make it return data in the expected JSON format and also work with sort/pagination.
   view plainprintabout
 <cffunction name="getUsers" access="remote" returnformat="json">
         <cfargument name="page" required="no" default="1" hint="Page user is on">
      <cfargument name="rows" required="no" default="10" hint="Number of Rows to display per page">
      <cfargument name="sidx" required="no" default="" hint="Sort Column">
      <cfargument name="sord" required="no" default="ASC" hint="Sort Order">
     
         <cfset var arrUsers = ArrayNew(1)>
         
         
10          <cfquery name="selUsers" datasource="RIADemo">
11              SELECT
12                  ID, FirstName, LastName, DisplayName, UserName, UserAccountingCode, Phone
13              FROM
14                  Users
15              <!--- Sorting Here --->
16              <cfif Arguments.sidx NEQ "">    
17                  ORDER BY #Arguments.sidx# #Arguments.sord#
18              <cfelse>
19                  ORDER BY ID #Arguments.sord#
20              </cfif>
21              
22              
23          </cfquery>
24          
25          <!--- Calculate the Start Position for the loop query.
26          So, if you are on 1st page and want to display 4 rows per page, for first page you start at: (1-1)*4+1 = 1.
27          If you go to page 2, you start at (2-)1*4+1 = 5 --->

28          <cfset start = ((arguments.page-1)*arguments.rows)+1>
29          
30          <!--- Calculate the end row for the query. So on the first page you go from row 1 to row 4. --->
31          <cfset end = (start-1) + arguments.rows>
32          
33          <!--- When building the array --->
34          <cfset i = 1>
35          
36          <cfloop query="selUsers" startrow="#start#" endrow="#end#">
37              <!--- Array that will be passed back needed by jqGrid JSON implementation --->        
38              <cfset arrUsers[i] = [#ID#,#FirstName#,#LastName#,#DisplayName#,#UserName#,#UserAccountingCode#,#Phone#]>
39              <cfset i = i + 1>            
40          </cfloop>
41          
42          <!--- Calculate the Total Number of Pages for your records. --->
43          <cfset totalPages = Ceiling(selUsers.recordcount/arguments.rows)>
44          
45          <!--- The JSON return.
46              Total - Total Number of Pages we will have calculated above
47              Page - Current page user is on
48              Records - Total number of records
49              rows = our data
50          --->

51   <cfset stcReturn = {total=#totalPages#,page=#Arguments.page#,records=#selUsers.recordcount#,rows=arrUsers}>
52          
53      <cfreturn stcReturn>
54          
55      </cffunction>
Here the getUsers function first as needed retrieves the records from the server. Adding the sort is similar to cfgrid. Next we have to implement the pagination feature. This requires us to calculate which row of the query to start at and till when to go. So we play around with the information we have and determine that. Then we loop through the query and build the array that will be our data records. Next we build the JSON data we are returning and return it. Nothing complex. To note here is that we are leaving the cell specification in JSON reader blank, cell: "", //not used. If we had specified something there, we would have to change how the return data looks. Currently, I have not found anything that would require the cell to be explicitly set, maybe the more complex jqGrid features require it. So, this was a basic grid with sorting and pagination. Next step would be to use its in-built filter functionality. It will also have to be another Blog Entry. Full Source Code. Feel free to let me know, if there is a better way to handle the query to JSON data conversion.
Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Phillip Senn's Gravatar What is in /PLite/Test/themes/basic/images?
# Posted By Phillip Senn | 9/16/09 1:48 AM
Kumar's Gravatar That folder has the images for the grid buttons.
# Posted By Kumar | 9/16/09 2:42 AM
Phillip Senn's Gravatar Where did you get:
/PLite/Test/themes/basic/images/last.gif
/PLite/Test/themes/basic/images/next.gif
/PLite/Test/themes/basic/images/off-prev.gif
/PLite/Test/themes/basic/images/off-first.gif
/PLite/Test/themes/basic/images/spacer.gif
/PLite/Test/themes/basic/images/up.gif
# Posted By Phillip Senn | 9/16/09 12:11 PM
Kumar Shah's Gravatar Those are the images that came with jqGrid before they implemented support for jQuery Themeroller.
# Posted By Kumar Shah | 9/16/09 12:36 PM
Alex's Gravatar Would it be possible to get a zip file with all the js and css files you used for the tutorial.. i have tried folowing your tutorial for hours and seem to have no luck.. i keep getting and js error saying object is expected in "users.cfm" Any help would be great i want to get past this one to move on throught the next tutorial that you have ,... thanks
# Posted By Alex | 9/22/09 11:50 AM
Phillip Senn's Gravatar Alex,

I've been working on it a lot as well.
Here's what I have so far:
http://www.cfmzengarden.com/jQueryZenGarden/jqGrid...

You'll want to run RIA.SQL first to build the users table.
Kumar: You live in Charlotte?
I run the Adobe Charlotte users group!
# Posted By Phillip Senn | 9/22/09 5:20 PM
Alex's Gravatar Hi, I have been using your tutorial (many thanks) but i have a question may or may not be possible.. But iam using this grid feature to display my seach results for a seach screen i am working on i wanted to use a grid rather than a plain table to display..... my question is : "Is it possible to add an href tag to one of the col results that goes to another page. and also pass a variable with it..

thanks
# Posted By Alex | 10/2/09 2:57 AM
Kumar's Gravatar You could build the link in your data e.g <a href="">Click</a> and that should render as a link.
# Posted By Kumar | 10/3/09 11:56 PM
Erich Haemmerle's Gravatar Hey guys. I'm trying desperately to get this to work. I tried on my own getting everything setup on my CF8 dev system. I got the grid to display and the sorting works and the pager works, but the pager is not showing the page totals or the total records. Instead of displaying "1 of 400" it's just displaying "of" with no page totals. And instead of displaying "Total Records: 4000", it's just displaying "Total Records:" I thought maybe i was doing something wrong so I downloaded Alex's version and ran his SQL to setup the table and ran his code and it's doing the same thing. Is this a CF8 thing or? I'm so close. I just need the paging to work right and I'm home free.
# Posted By Erich Haemmerle | 10/20/09 1:50 PM
Meghu's Gravatar My grid is displaying fine but its not displaying the data....any ideas?
I am using the same files with same database structure. My jqGrid version is 3.4.4.
# Posted By Meghu | 10/23/09 11:34 AM
Kumar Shah's Gravatar @Haemmerle, what jqGrid version are you using?

@Meghu, Make sure ColdFusion debugging is turned off
# Posted By Kumar Shah | 10/23/09 1:08 PM
Meghu's Gravatar Kumar...I made debugging turned off..but still no luck
# Posted By Meghu | 10/23/09 1:44 PM
Meghu's Gravatar Its started working....something wrong with my application.cfm.......created new application.cfm in my test folder...and boom ..its started working....Thanks Kumar
# Posted By Meghu | 10/23/09 4:42 PM
Kumar's Gravatar @Meghu, That genrally happens if the Application.CFC/CFC had HTML code in it. Good to know it started working now.
# Posted By Kumar | 10/24/09 4:25 PM
Cheryl Miller's Gravatar First of all THANKS ALOT! Your example enabled me to get this up and running pretty quick.

Secondly, I did have a problem with data not displaying with debugging turned on. I turned it off and the data magically appeared. I found a work around if anyone is interested. I placed <cfsetting showdebugoutput="no"> in the cfc (just after the opening component tag), and it resolved the problem. Now I can keep developing with debugging turned on. : )
# Posted By Cheryl Miller | 2/25/10 8:28 PM
Michael's Gravatar After I changed implicit structure to explicit structure in users.cfc for implementing in CF MX7, the header and footer displayed. But the data and grid never show up. I am using IE7, CF MX7 and Oracle database. What is the possible cause for this? I also tried it on CF9 without modifying any code, but still got no data and grid. Please help.
# Posted By Michael | 3/24/10 3:39 PM
Evan's Gravatar Hey there. trying to get mine to work. But when I run the code, it keeps telling me the value I return from the cfc is not json:
"The value returned from the getJobsByAccountID function is not of type json."

the function looks exactly like the example. I have also tried using
SerializeJSON(stcReturn), and get the same results. It seems like whatever I do, it doesn't see the returned result as json.

Debugging is turned off by the way. Any advice?


Here's the cfc code.

<cfcomponent>

   <cffunction name="getJobsByAccountID" access="remote" returntype="json">
      <cfargument name="accountID" type="numeric" required="true" >
      <cfargument name="page" required="no" default="1" hint="Page user is on">
      <cfargument name="rows" required="no" default="10" hint="Number of Rows to display per page">
      <cfargument name="sidx" required="no" default="" hint="Sort Column">
      <cfargument name="sord" required="no" default="ASC" hint="Sort Order">

      <cfquery name="getJobsByCompanyID" datasource="pcpma">
         SELECT *
          FROM Company c
          INNER JOIN Jobs j ON c.companyID = j.companyID
          WHERE c.accountID = <cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.accountID#">
          ORDER BY DateCreated DESC
      </cfquery>

      <cfset aryJobs = ArrayNew(1)>

      <!--- Calculate the Start Position for the loop query. So, if you are on 1st page and want to display 4 rows per page,
            for first page you start at: (1-1)*4+1 = 1. If you go to page 2, you start at (2-)1*4+1 = 5 --->
      <cfset start = ((arguments.page-1)*arguments.rows)+1>

      <!--- Calculate the end row for the query. So on the first page you go from row 1 to row 4. --->
      <cfset end = (start-1) + arguments.rows>

      <!--- When building the array --->
      <cfset i = 1>

      <cfloop query="getJobsByCompanyID" startrow="#start#" endrow="#end#">
         <!--- Array that will be passed back needed by jqGrid JSON implementation --->
         <cfset aryJobs[i] = [#getJobsByCompanyID.jobCode#,#getJobsByCompanyID.companyName#,#getJobsByCompanyID.title#]>
         <cfset i = i + 1>
      </cfloop>

      <!--- Calculate the Total Number of Pages for your records. --->
      <cfset totalPages = Ceiling(getJobsByCompanyID.recordcount/arguments.rows)>

      <!--- The JSON return.
         Total - Total Number of Pages we will have calculated above
         Page - Current page user is on
         Records - Total number of records
         rows = our data
      --->
      <cfset stcReturn = {total=#totalPages#,page=#arguments.page#,records=#getJobsByCompanyID.recordcount#,rows=aryJobs}>

      <!---<cfdump var="#stcReturn#">--->

      <cfreturn stcReturn>

   </cffunction>
# Posted By Evan | 7/2/10 4:57 PM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.5.003.  Design based on ARCLITE by: digitalnature