Solved

JQGrid and Coldfusion - Can't get display!

Posted on 2014-01-28
10
812 Views
Last Modified: 2014-01-28
I can't for the life of me find what I'm doing wrong in this jqgrid. I've tried to break it down as simply as possible...

Here's my .cfm page:

<head>
<script>
application.cfstatic
		.include('scripts.js');
</script>
<title>Grid Test</title>
 <cfscript>
        param type="string" name="REQUEST.addScriptsHeader" default="";
        param type="string" name="REQUEST.addCSSHeader" default="";
        param type="string" name="REQUEST.addScriptsFooter" default="";
        param type="string" name="REQUEST.addCSSFooter" default="";
        param type="string" name="REQUEST.adder" default="";
        param type="string" name="REQUEST.scriptMinifier" default=".min";
        param type="boolean" name="REQUEST.jqGridLoaded" default=false;

    
        if(!REQUEST.jqGridLoaded){
            savecontent variable="VARIABLES.adder" {
                WriteOutput("<script type='text/javascript' src='/assets/scripts/_lib/jquery/plugins/loadmask-0.4/jquery.loadmask#REQUEST.scriptMinifier#.js'></script>
                    <script type='text/javascript' src='/assets/scripts/_lib/jquery/plugins/jqgrid-4.3.0/js/i18n/grid.locale-en.js'></script>
                    <script type='text/javascript' src='/assets/scripts/_lib/jquery/plugins/jqgrid-4.3.0/js/jquery.jqGrid#REQUEST.scriptMinifier#.js'></script>");
            }
            REQUEST.addScriptsHeader &= VARIABLES.adder;
            savecontent variable="VARIABLES.adder" {
                WriteOutput("<link href='/assets/scripts/_lib/jquery/plugins/loadmask-0.4/jquery.loadmask.css' type='text/css' rel='stylesheet' />
                    <link href='/assets/scripts/_lib/jquery/plugins/jqgrid-4.3.0/css/ui.jqgrid.css' type='text/css' rel='stylesheet' />");
            }
            REQUEST.addCSSFooter &= VARIABLES.adder;
            REQUEST.jqGridLoaded = true;
        }
</cfscript>
</head>

<body>
	<table id="list" class="scroll" cellpadding="0" cellspacing="0"></table>
 		<div id="pager" class="scroll" style="text-align:center;"></div> 
		<div id="mysearch"></div>
</body>

Open in new window


--- Here's my .cfc

<cfcomponent displayname="UserMgr" output="false"> 
  
<cffunction name="getTestUsers" access="remote" output="false" returnformat="json">
    <cfargument name="page" required="no" default="1">
    <cfargument name="rows" required="no" default="10">
    <cfargument name="sidx" required="no" default="">
    <cfargument name="sord" required="no" default="ASC">
    <cfset var q = "">
    <cfquery datasource="#session.currentdsn#" name="q">
    SELECT top 10 id, firstname, lastname
    FROM f_users
    <cfif len(arguments.sidx)>
    ORDER BY #arguments.sidx# #arguments.sord#
    </cfif>
    </cfquery>
    
    <cfreturn queryConvertForJQGRID(q, arguments.page, arguments.rows) />
  </cffunction>
    
  <cffunction name="queryConvertForJQGRID" access="package" returntype="struct" output="no"> 
    <cfargument name="q" type="query" required="yes"> 
    <cfargument name="page" type="numeric" required="no" default="1"> 
    <cfargument name="rows" type="numeric" required="no" default="10"> 
    <cfset var result = structnew()> 
    <cfset var rowStruct = structnew()> 
    <cfset var col = ""> 
    <cfset result.page = arguments.page> 
    <cfset result.total = ceiling(arguments.q.recordcount/arguments.rows)> 
    <cfset result.records = arguments.q.recordcount> 
    <cfset result.rows = arraynew(1)> 
    <cfoutput query="arguments.q" startrow="#(arguments.page-1)*arguments.rows+1#" maxrows="#arguments.rows#"> 
      <cfset rowStruct = structnew()> 
      <cfset rowStruct['id'] = id>
	  <cfset rowStruct['cell'] = arraynew(1)>
      <cfset rowStruct['cell'][1] = arguments.q['id'][currentrow]> 
      <cfset rowStruct['cell'][2] = arguments.q['firstname'][currentrow]> 
      <cfset rowStruct['cell'][3] = arguments.q['lastname'][currentrow]> 
      <cfset arrayappend(result.rows, rowStruct)> 
    </cfoutput>
	<cfset output = SerializeJSON(result)>
	<cfset output = replace(output,'.0','','all')>
	<cfset output = replace(output,'ROWS','rows','all')>
	<cfset output = replace(output,'PAGE','page','all')>
	<cfset output = replace(output,'TOTAL','total','all')>
	<cfset output = replace(output,'RECORDS','records','all')>
	<cfset output = replace(output,'CELL','cell','all')>
    <cfreturn output /> 
  </cffunction> 

</cfcomponent>

Open in new window


Here's my .js

$("document").ready(function() {

		jQuery("#list").jqGrid({
			url:'processor.cfc?method=getTestUsers',
			width:"auto",
			height:"auto",
			datatype: "json",
			colNames:['ContactID','First Name','Last Name'],
			colModel:[
				{name:'id',index:'id', width:55},
				{name:'firstname',index:'firstname', width:200},
				{name:'lastname',index:'lastname', width:250},
				
			],
			rowNum:10,
			rowList:[10,20,30],
			pager: '#pager',
			sortname: 'id',
			viewrecords: true,
			sortorder: "asc",
			caption:"User List"
			});

		});

Open in new window


I'm not getting a display on the front end and when I go to processor.cfc?method=getTestUsers I get:

The value returned from the queryConvertForJQGRID function is not of type struct.

If the component name is specified as a return type, it is possible that either a definition file for the component cannot be found or is not accessible.
 
The error occurred in C:/inetpub/wwwroot/tracitest/processor.cfc: line 17
15 :     </cfquery>
16 :    
17 :     <cfreturn queryConvertForJQGRID(q, arguments.page, arguments.rows) />
18 :   </cffunction>
19 :    

Ugh Please help!
0
Comment
Question by:traport
  • 5
  • 5
10 Comments
 
LVL 52

Accepted Solution

by:
_agx_ earned 500 total points
ID: 39815939
The problem is pretty much what the error message says.  queryConvertForJQGRID  isn't returning a structure.  The "output" variable contains a string, so the function is actually returning a string, not a structure. Just return the "result " variable instead.

Side note, I notice you've encountered CF's unfortunate habit of converting structure keys to all upper case. If you want to preserve the case of key names (for javascript), use array notation, instead of dot-notation, ie

<cfset result["page"] = arguments.page>
<cfset result["total"] = ceiling(arguments.q.recordcount/arguments.rows)>
... other keys ...

EDIT: Though you don't need the "output" variable anymore, ... if you did, don't forget to VAR scope it too
0
 

Author Comment

by:traport
ID: 39815983
Hey _agx_,

Thanks for the response. I don't know how to return the result variable versus the structure (and I guess I don't know how to return a structure, either).

What's the benefit to using array notation versus dot notation? Are you saying then case doesn't matter for javascript?

And (20 questions!)... Would you give an example of the VAR scope for output variables and if you could explain why that'd be much appreciated, too.

I'm a homegrown developer so it's people like you who really help me along. Thanks.
0
 
LVL 52

Expert Comment

by:_agx_
ID: 39816068
how to return the result variable

Inside your function, you're already creating a structure named "result". All you need to do is return that variable instead of "output":

  <cffunction ....>
      .... existing code
      <!--- return structure instead of "output" variable --->
      <cfreturn result />
  </cffunction>

What's the benefit to using array notation versus dot notation? Are you saying then case doesn't matter for javascript?


No, you were right the first time :) Javascript definitely *is* case sensitive.  That's the reason you need to use array notation. Unfortunately, when you use dot notation, CF ignores whatever case you used and converts everything to upper case:

         <cfset result.page = "something...">
        <!--- serialized as JSON ...  --->
        { PAGE="something..." }

... so your javascript won't work because it's looking for "page" (all lower case).  When you use array notation, CF sends the key name exactly as you entered it (ie all lower case)

         <cfset result["page"] = "something...">
        <!--- serialized as JSON ...  --->
        { page="something..." }



Would you give an example of the VAR scope for output variables and if you could explain why that'd be much appreciated, too.

Any variable used only within the function (ie function local variables) should be VAR scoped.  You're already doing that with the other variables here. Just do the same with "output" at the top of the function:

       <!--- use VAR to put these variables in the local scope --->
       <cfset var result = structnew()>
        ....
       <cfset var output = "">

VAR scoping is a complex topic, but an interesting one :) The reason you var scope is - thread safety. In other words, to prevent one thread from messing up the results of another thread. Usually you only encounter thread issues when you're storing components in a shared scope (like application).  That's not the case here ... but it's good to plan ahead and always make code thread safe from the get-go.

Here's a brief blurb about var scoping (or LOCAL-scoping in CF9+):
http://forta.com/blog/index.cfm/2009/6/21/The-New-ColdFusion-LOCAL-Scope

I'm a homegrown developer so it's people like you who really help me along.

Anytime. I always enjoy discussions on "how things work and why" :)
0
 

Author Comment

by:traport
ID: 39816205
Thank you. Very insightful answer & help. I'm going to put these things into practice.

For some reason <cfreturn result /> came back with an error Variable result is undefined on processor.cfc and nothing returned in the grid.
0
 
LVL 52

Expert Comment

by:_agx_
ID: 39816216
Hm.. that variable is clearly defined. Are you sure you put it in the right function - ie queryConvertForJQGRID?

<cffunction name="queryConvertForJQGRID" access="package" returntype="struct" output="no">
    <cfargument name="q" type="query" required="yes">
    <cfargument name="page" type="numeric" required="no" default="1">
    <cfargument name="rows" type="numeric" required="no" default="10">
   <cfset var result = structnew()>  <!--- **** DEFINED HERE --->
    <cfset var rowStruct = structnew()>
    <cfset var col = "">

    <cfset result["page"] = arguments.page>
    <cfset result["total"] = ceiling(arguments.q.recordcount/arguments.rows)>
    <cfset result["records"] = arguments.q.recordcount>
    <cfset result["rows"] = arraynew(1)>


    <cfoutput query="arguments.q" startrow="#(arguments.page-1)*arguments.rows+1#" maxrows="#arguments.rows#">
         <cfset rowStruct = structnew()>
         <cfset rowStruct['id'] = id>
       <cfset rowStruct['cell'] = arraynew(1)>
         <cfset rowStruct['cell'][1] = arguments.q['id'][currentrow]>
         <cfset rowStruct['cell'][2] = arguments.q['firstname'][currentrow]>
         <cfset rowStruct['cell'][3] = arguments.q['lastname'][currentrow]>
         <cfset arrayappend(result.rows, rowStruct)>
    </cfoutput>

   <cfreturn result />
  </cffunction>
0
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 

Author Comment

by:traport
ID: 39816233
I'd incorrectly placed the cfreturn result in the function ABOVE.

Okay so that works to output on the processor.cfc?method=GetUsers page... Great!

Shows: "{"rows":[{"cell":[20,"Christian","Ready"],"id":20},{"cell":[21,"Sean","Louisin"],"id":21},{"cell":[22,"Mike","Coleman"],"id":22},{"cell":[23,"Jim","Studebaker"],"id":23},{"cell":[24,"",""],"id":24},{"cell":[29,"Yancy","Wharton"],"id":29},{"cell":[26,"Donald","Hawkins"],"id":26},{"cell":[30,"Scott","Harvey"],"id":30},{"cell":[31,"",""],"id":31},{"cell":[32,"Dana","Lecroy"],"id":32}],"page":1,"records":7289,"total":729.0}..."

Now, why doesn't that autopopulate a grid on the index.cfm page?
0
 
LVL 52

Expert Comment

by:_agx_
ID: 39816335
Not sure. I'll have to find a copy of jGrid and try it.  Do you see any errors in your javascript console (Firebug, etc..)?
0
 

Author Comment

by:traport
ID: 39816383
I think it's my min files. I'll take it from here. Really appreciate your help!
0
 

Author Closing Comment

by:traport
ID: 39816387
The whole thread of input was awesome. Thanks so much.
0
 
LVL 52

Expert Comment

by:_agx_
ID: 39816447
Welcome :)
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Introduction Chart.js, used properly, can visually add a difference to your charting applications. It engages your visitors and allows them to interact with data they otherwise wouldn't be able to without expensive and complicated systems. For this…
This article demonstrates how to create a simple responsive confirmation dialog with Ok and Cancel buttons using HTML, CSS, jQuery and Promises
The viewer will learn the basics of jQuery, including how to invoke it on a web page. Reference your jQuery libraries: (CODE) Include your new external js/jQuery file: (CODE) Write your first lines of code to setup your site for jQuery.: (CODE)
The viewer will learn the basics of jQuery including how to code hide show and toggles. Reference your jQuery libraries: (CODE) Include your new external js/jQuery file: (CODE) Write your first lines of code to setup your site for jQuery…

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now