It has been a while since I made my last blog post as work has kept me very busy. Over the weekend I saw that a new jqGrid version had been released and that it contained in-built advanced searching (called multiple search). I also thought it was a good time to revisit my earlier blog post on a full CRUD grid and update it to 3.6 specifications.
This implementation uses the following jqGrid features:
- Inbuilt Form based Add, Edit, Delete and Search functionality
- Search is done using new Multiple Search feature
- Exploration of a few additional colModel parameters
View the Demo : this is all realtime, any updates made will be reflected in the grid.
Now, lets look at the code
The HTML is still very simple.
2<div id="pager" class="scroll"></div>
2
3 $(document).ready(function()
4 {
5 $("#list").jqGrid(
6 {
7 url:'Users2.cfc?method=getUsers', //CFC that will return the users
8 datatype: 'json', //We specify that the datatype we will be using will be JSON
9 colNames:['ID','First Name','Last Name', 'Display Name','User Name'], //Column Names
10 //The Column Model to define the data. Note you can make columns non sortable, specify width, alignment, etc.
11 colModel :[
12 {name:'id',index:'id', width:75, sorttype:"int",editable:false,searchoptions:{sopt:['eq','lt','le','gt','ge']}},
13 {name:'FirstName',index:'FirstName', width:150, sorttype:"text",searchoptions:{sopt:['eq','bw','ew','cn']},
14 editable:true,edittype:"text",editoptions:{size:50},editrules:{required:true},formoptions:{elmprefix:"(*)"}},
15 {name:'LastName',index:'LastName', width:150, align:"left",sorttype:"text",searchoptions:{sopt:['eq','bw','ew','cn']},
16 editable:true,edittype:"text",editoptions:{size:50},editrules:{required:true},formoptions:{elmprefix:"(*)"}},
17 {name:'DisplayName',index:'DisplayName', width:150, align:"left",sorttype:"text",searchoptions:{sopt:['eq','bw','ew','cn']},
18 editable:true,edittype:"text",editoptions:{size:50},editrules:{required:true},formoptions:{elmprefix:"(*)"}},
19 {name:'UserName',index:'UserName', width:150,align:"left",sorttype:"text",searchoptions:{sopt:['eq','bw','ew','cn']},
20 editable:true,edittype:"text",editoptions:{size:50},editrules:{required:true},formoptions:{elmprefix:"(*)"}}
21 ],//searchoptions parameter is used to limit the operators available during search
22 pager: $('#pager'), //The div we have specified, tells jqGrid where to put the pager
23 rowNum:20, //Number of records we want to show per page
24 rowList:[20,30,40,50], //Row List, to allow user to select how many rows they want to see per page
25 sortorder: "asc", //Default sort order
26 sortname: "ID", //Default sort column
27 viewrecords: true, //Shows the nice message on the pager
28 imgpath: '/Test/themes/basic/images', //Image path for prev/next etc images
29 caption: 'Users', //Grid Name
30 recordtext: "Record {0} - {1} of {2}",//Pager information to show
31 rownumbers: true,//Show row numbers
32 rownumWidth: "40",//Width of the row numbers column
33 sortable: true,
34 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
35 mtype:'POST',
36 toolbar:[true,"top"], //Shows the toolbar at the top. We will use it to display user feedback
37 //The JSON reader. This defines what the JSON data returned from the CFC should look like
38 jsonReader: {
39 root: "ROWS", //our data
40 page: "PAGE", //current page
41 total: "TOTAL", //total pages
42 records:"RECORDS", //total records
43 userdata:"USERDATA", //Userdata we will pass back for feedback
44 cell: "", //Not Used
45 id: "0" //Will default to first column
46 },
47 editurl:"Users2.cfc?method=addeditUser" //The Add/Edit function call
48 }
49 ).navGrid('#pager',
50 {
51 search:true,searchtitle:"Search",//title set for hover over display
52 edit:true,edittitle:"Edit User",
53 add:true,addtitle:"Add User",
54 del:true,deltitle:"Delete User"
55 },
56 // Edit Options. savekey parameter will keybind the Enter key to submit.
57 {editCaption:"Edit User",edittext:"Edit",closeOnEscape:true, savekey: [true,13],errorTextFormat:commonError,width:"450"
58 ,reloadAfterSubmit:true,bottominfo:"Fields marked with (*) are required",top:"60",left:"70"},
59 {addCaption:"Add User",closeOnEscape:true,savekey: [true,13],errorTextFormat:commonError,width:"450"
60 ,reloadAfterSubmit:true,bottominfo:"Fields marked with (*) are required",top:"60",left:"70"}, //Add Options
61 {url:"Users2.cfc?method=delUser",caption:"Delete User",closeOnEscape:true,errorTextFormat:commonError,top:"60",left:"70",
62 reloadAfterSubmit:true}, //Delete Options
63 //Search Options. multipleSearch parameter lets it know we are going to use the new advanced search feature
64 {errorTextFormat:commonError,Find:"Search",closeOnEscape:true,caption:"Search Users",multipleSearch:true,closeAfterSearch:true}
65 );
66
67 //Function will be called when add/edit encounters an error. The returned message is what will be shown to user
68 function commonError(data)
69 {
70 return "Error Occured during Operation. Please try again";
71 }
72
73 }
74 );
75 </script>
Hopefully the comments in the code help understand what is going on. I am taking advantage of new colModel definitions that we can specify to help
the multiple search feature. You can see this in the searchoptions parameter specification. The sopt array array specifies which operators can be associated with the search field, so for example, ID which is an integer can only handle equal, less than, less than equal to, greater than and greater than equal to operators. While the rest of the search fields which are text fields use operators more suited to text based searching.
We are also using the formoptions parameter and passing the elmprefix value. This value we have passed will be appended to our input elements. In this case we will use it to tell the user which fields are required.
In addition, we have specified additional parameters for the Add/Edit/Delete options. These include specifying the savekey (this is set to Enter, so pressing Enter will submit the Add/Edit form), closeOnEscape is set to true (you can close those modal dialogs with the Esc key). We have also specified our error handler for Add/Edit which is the function commonError. It will return a generic error message to the user. In order to use the multiple search implementation in jqGrid we have specified the multipleSearch parameter to true (so simple it is).
We also have our CFC component that will form the bridge between our grid and database.
2
3 <cffunction name="getUsers" access="remote" returnformat="json">
4 <cfargument name="page" required="no" default="1" hint="Page user is on">
5 <cfargument name="rows" required="no" default="10" hint="Number of Rows to display per page">
6 <cfargument name="sidx" required="no" default="" hint="Sort Column">
7 <cfargument name="sord" required="no" default="ASC" hint="Sort Order">
8 <cfargument name="nd" required="no" default="0">
9 <cfargument name="_search" required="no" default="false">
10 <cfargument name="filters" required="no" default="">
11
12 <cfset var arrUsers = ArrayNew(1)>
13 <cfset var strMsg = "">
14 <cfset var strMsgType = "Success">
15 <cfset var records = "">
16 <cfset var blnSearch = Arguments._search>
17 <cfset var strSearch = "">
18
19 <!--- Determine whether this a search call and if so build our search parameters --->
20 <cfif Arguments.filters NEQ "" AND blnSearch>
21 <cfset stcSearch = DeserializeJSON(Arguments.filters)>
22 <cfif isDefined("stcSearch.groupOp")>
23 <cfset strSearch = buildSearch(stcSearch)>
24 </cfif>
25 </cfif>
26
27 <cfquery name="selUsers" datasource="RIADemo" result="res">
28 SELECT
29 ID, FirstName, LastName, DisplayName, UserName
30 FROM
31 Users
32 <cfif blnSearch AND strSearch NEQ "">
33 #PreserveSingleQuotes(strSearch)#
34 </cfif>
35 ORDER BY #sidx# #sord#
36 </cfquery>
37
38
39
40 <cfset records = selUsers>
41
42
43 <!--- Calculate the Start Position for the loop query.
44 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.
45 If you go to page 2, you start at (2-)1*4+1 = 5 --->
46 <cfset start = ((arguments.page-1)*arguments.rows)+1>
47
48 <!--- Calculate the end row for the query. So on the first page you go from row 1 to row 4. --->
49 <cfset end = (start-1) + arguments.rows>
50
51 <!--- When building the array --->
52 <cfset i = 1>
53
54 <cfloop query="records" startrow="#start#" endrow="#end#">
55 <!--- Array that will be passed back needed by jqGrid JSON implementation --->
56 <cfset arrUsers[i] = [#ID#,#FirstName#,#LastName#,#DisplayName#,#UserName#]>
57 <cfset i = i + 1>
58 </cfloop>
59
60 <!--- Calculate the Total Number of Pages for your records. --->
61 <cfset totalPages = Ceiling(records.recordcount/arguments.rows)>
62
63 <!--- The JSON return --->
64 <cfset strReturn = {total=#totalPages#,page=#Arguments.page#,records=#records.recordcount#,rows=arrUsers}>
65
66 <cfreturn strReturn>
67
68 </cffunction>
69
70 <cffunction name="addeditUser" access="remote" hint="Add or Edit User Data" returnformat="json">
71 <cfargument name="FirstName" required="yes" hint="Field that was Added or editted">
72 <cfargument name="LastName" required="yes" hint="Field that was Added or editted">
73 <cfargument name="DisplayName" required="yes" hint="Field that was Added or editted">
74 <cfargument name="UserName" required="yes" hint="Field that was Added or editted">
75 <cfargument name="ID" required="yes" hint="the User that was Added or editted">
76 <cfargument name="oper" required="no" default="edit" hint="Whether this is an add or edit">
77
78 <cfset var strMsg = "">
79 <cfset var strMsgType = "Success">
80 <cfset var userdata = "">
81
82 <cfif oper EQ "edit">
83 <cfset strMsg = "User Editted">
84 <!--- Take the data, update your record. Simple. --->
85 <cftry>
86 <cfquery name="editUser" datasource="RIADemo">
87 UPDATE
88 Users
89 SET
90 FirstName = '#Arguments.FirstName#',
91 LastName = '#Arguments.LastName#',
92 DisplayName = '#Arguments.DisplayName#',
93 UserName = '#Arguments.UserName#'
94 WHERE
95 ID = #Val(Arguments.ID)#
96 </cfquery>
97 <cfcatch type="any">
98 <!--- Error, return message --->
99 <cfset strMsgType = "Error">
100 <cfset strMsg = "Error Occured when editing user. An Error report has been submitted to support.">
101 <cfthrow message="Error Occured During Edit">
102 </cfcatch>
103 </cftry>
104 <cfelse>
105 <cfset strMsg = "User Added">
106 <cftry>
107 <cfquery name="addUser" datasource="RIADemo">
108 INSERT INTO USERS(FirstName,LastName,DisplayName,UserName)
109 VALUES ('#Arguments.FirstName#','#Arguments.LastName#','#Arguments.DisplayName#','#Arguments.UserName#')
110 </cfquery>
111 <cfcatch>
112 <!--- Error, return message --->
113 <cfset strMsgType = "Error">
114 <cfset strMsg = "Error occured when Adding User. An Error report has been submitted to support.">
115 <cfthrow message="Error Occured During Add">
116 </cfcatch>
117 </cftry>
118 </cfif>
119 <!--- We just need to pass back some user data for display purposes --->
120 <cfset userdata = {type="#strMsgType#",msg="#strMsg#"}>
121 <cfset strReturn = {userdata=#userdata#}>
122
123 <cfreturn strReturn>
124
125 </cffunction>
126
127 <cffunction name="delUser" access="remote" hint="Delete Selected User" returnformat="json">
128 <cfargument name="id" required="yes" hint="id to delete">
129
130 <cfset var strMsg = "">
131 <cfset var strMsgType = "Success">
132 <cfset var userdata = "">
133 <cfset var delUser = "">
134
135 <cftry>
136 <cfset strMsg = "Deleted User">
137 <cfquery name="delUser" datasource="RIADemo">
138 DELETE FROM Users WHERE ID = #Val(Arguments.id)#
139 </cfquery>
140 <cfcatch>
141 <!--- Error, return message --->
142 <cfset strMsgType = "Error">
143 <cfset strMsg = "Error occured when Deleting User. An Error report has been submitted to support.">
144 </cfcatch>
145 </cftry>
146
147 <cfset userdata = {type="#strMsgType#",msg="#strMsg#"}>
148 <cfset strReturn = {userdata=#userdata#}>
149
150 <cfreturn strReturn>
151
152 </cffunction>
153
154 <cffunction name="buildSearch" access="private" hint="Build our Search Parameters">
155 <cfargument name="stcSearch" required="true">
156
157 <!--- strOp will be either AND or OR based on user selection --->
158 <cfset var strOp = stcSearch.groupOp>
159 <cfset var arrFilter = stcSearch.rules>
160 <cfset var strSearch = "">
161 <cfset var strSearchVal = "">
162
163 <!--- Loop over array of passed in search filter rules to build our query string --->
164 <cfloop array="#arrFilter#" index="arrIndex">
165 <cfset strField = arrIndex["field"]>
166 <cfset strOp = arrIndex["op"]>
167 <cfset strValue = arrIndex["data"]>
168
169 <cfset strSearchVal = buildSearchArgument(strField,strOp,strValue)>
170
171 <cfif strSearchVal NEQ "">
172 <cfif strSearch EQ "">
173 <cfset strSearch = "WHERE (#PreserveSingleQuotes(strSearchVal)#)">
174 <cfelse>
175 <cfset strSearch = strSearch & "#strOp# (#PreserveSingleQuotes(strSearchVal)#)">
176 </cfif>
177 </cfif>
178
179 </cfloop>
180
181 <cfreturn strSearch>
182
183 </cffunction>
184
185 <cffunction name="buildSearchArgument" access="private" hint="Build our Search Argument based on parameters">
186 <cfargument name="strField" required="true" hint="The Field which will be searched on">
187 <cfargument name="strOp" required="true" hint="Operator for the search criteria">
188 <cfargument name="strValue" required="true" hint="Value that will be searched for">
189
190 <cfset var searchVal = "">
191
192 <cfif Arguments.strValue EQ "">
193 <cfreturn "">
194 </cfif>
195
196 <cfscript>
197 switch(Arguments.strOp)
198 {
199 case "eq":
200 //ID is numeric so we will check for that
201 if(Arguments.strField EQ "id")
202 {
203 searchVal = "#Arguments.strField# = #Arguments.strValue#";
204 }else{
205 searchVal = "#Arguments.strField# = '#Arguments.strValue#'";
206 }
207 break;
208 case "lt":
209 searchVal = "#Arguments.strField# < #Arguments.strValue#";
210 break;
211 case "le":
212 searchVal = "#Arguments.strField# <= #Arguments.strValue#";
213 break;
214 case "gt":
215 searchVal = "#Arguments.strField# > #Arguments.strValue#";
216 break;
217 case "ge":
218 searchVal = "#Arguments.strField# >= #Arguments.strValue#";
219 break;
220 case "bw":
221 searchVal = "#Arguments.strField# LIKE '#Arguments.strValue#%'";
222 break;
223 case "ew":
224 searchVal = "#Arguments.strField# LIKE '%#Arguments.strValue#'";
225 break;
226 case "cn":
227 searchVal = "#Arguments.strField# LIKE '%#Arguments.strValue#%'";
228 break;
229 }
230 </cfscript>
231
232 <cfreturn searchVal>
233
234 </cffunction>
235
236</cfcomponent>
Source Code (cfm/cfc) Here.
Feel free to contact me if you have any questions and I will try to help you out.
#1 by Sebastiaan on 12/9/09 - 7:40 AM
#2 by Kumar on 12/9/09 - 10:18 PM
#3 by udip on 12/14/09 - 2:54 AM
#4 by Kumar Shah on 12/14/09 - 10:42 AM
Ray Camden has a good post on using jQuery and ColdFusion for multiple file uploading:
http://www.coldfusionjedi.com/index.cfm/2009/11/20...
#5 by udip on 12/14/09 - 5:28 PM
open in a new window. Similar to cfwindow so I can easily add custom fields in there.
#6 by Kumar Shah on 12/14/09 - 6:46 PM
jqGrid provides functions to add your own buttons to the navigation bar, and also what you do when someone clicks on it.
#7 by Jim Console on 12/15/09 - 9:46 PM
#8 by Kumar on 12/15/09 - 11:07 PM
It should have a way to make the data appear as a link to a specified target.
#9 by Jim Console on 12/21/09 - 2:43 PM
I am pretty new to JQuery, and not all that strong with JS, especially debugging (noob disclaimer) - When I run your example through Firebug I get "$ is not defined" on the line $(document).ready(function()" - the first line. It seems to me that I am missing a dependency somewhere, but I know I have the three JS files ref'd correctly as I have copied them into the cfm directory and removed "js/" from src reference.
#10 by Sung on 12/21/09 - 3:13 PM
#11 by Kumar Shah on 12/21/09 - 3:36 PM
@Sung, you are right about that, I forgot the update the CFC to handle multiple filter parameters. Will work on that this week.
#12 by Patrick on 2/19/10 - 12:37 AM
Thank you!
#13 by Patrick on 2/22/10 - 7:26 PM
#14 by Will on 11/22/11 - 9:08 AM
#15 by Sue on 2/28/12 - 5:13 PM
Your jqgrid examples are a great help. I have used it to learn about the jqgrid and set up one using coldfusion. Can you direct me to any information on setting up the dynamic selects in the edit form using jsonreader? I have a column in the colModel like this:
{name:'key1',index:key1',searchoptions:{sopt:['eq','bw','ew','cn']},
editable:true, edittype:'select',formatter:'select',editoptions:{dataUrl:'Users2.cfc?method=getGridAccess'},editrules:{required:true},formoptions:{elmprefix:"(*)"}},
nothing is displaying although i have cfdumped my query and it works.
thanks vey much for any advice. sue
#16 by Kumar Shah on 3/4/12 - 2:46 PM
#17 by Sue on 3/5/12 - 12:16 PM
Thanks for the response. I am using firebug and my data is being returned like this: {"COLUMNS":["SO_WHS_KEY"],"DATA":[["WC01"],["WC02"],["WC03"],["WC04"],["WC30"],["WCAC"],["WCBP"],..... but nothing is displayed in the form selects. Do you have any information on buildSelect? I think that's what I need but don't know where to get the information to understand how to set it up. Any ideas would be great.
I noticed my data for the jqgrid is being returned as rows and it is being returned and displayed correctly.
#18 by Sue on 3/13/12 - 7:22 PM
I have added additional paramaters on the selusers function in the cfc in order to display certain data. Now the search doesn't work. Do you think I should set up the jmodal search separately?
#19 by Kumar Shah on 3/13/12 - 11:31 PM
#20 by Misty on 5/12/12 - 5:39 PM
I am big fan of cfgrid and ext JS grid, awesome thing, recently i came across your post and saw the jqgrid implementation, really appreciate nice work done here..
doing some testing on search filter, found this issue,
SELECT ID, FirstName, LastName, DisplayName, UserName FROM Users WHERE (id >= 80)cn (FirstName LIKE '%walinba%') ORDER BY DisplayName desc
hopoe that might add my 2 cents to testing
:)
great job mate, awesome work u did
cheers
#21 by Chris Keeler on 5/29/12 - 3:22 PM
#22 by Kumar on 6/10/12 - 11:29 PM
#23 by Fabiano on 8/15/12 - 10:14 PM
How I fix this error?
Error during DDL export
Tks
Fabiano Magno Pechibella
#24 by EJ on 1/3/13 - 3:27 PM
Great example! Thank you for your site!
I am trying to add more than one filter in the cfc file. Would you be able to show the updated cfc with the multiple filters?
Thank you!