One of the coolest things about the ColdFusion 8 implementation of the CFGrid tag is that you can do a lot of customization, if you know your way around the Ext objects. I have found several blog entries about using custom renderers with the CFGrid tag. However, could not find a working example of one for date fields, so I decided to build one.

This example uses the coupons table in the cfbookclub database that comes with ColdFusion. There are two files in this example, coupons.cfc contains the functions needed for the editable CFGrid, and CouponForm.cfm contains the CFGrid Code and the JavaScript needed to do the date rendering. You will notice I had to specifically import the /CFIDE/scripts/ajax/ext/package/date.js file because it is not automatically imported with the cfgrid functionality. If you don't include that script tag you will get the JavaScript error "v.dateFormat is not a function".

CouponForm.cfm

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
   <title>Custom Date Renderer</title>
   <!--- import the Ext date package --->
   <script src="/CFIDE/scripts/ajax/ext/package/date.js" type="text/javascript"></script>
   <!--- create javascript function for rendering dates --->
   <script language="JavaScript" type="text/javascript">
   setDateRenderer = function(){
      mygrid = ColdFusion.Grid.getGridObject('CouponsGrid');
      cm = mygrid.getColumnModel();
      cm.setRenderer(3, Ext.util.Format.dateRenderer('m/d/Y'));
      mygrid.reconfigure(mygrid.getDataSource(),cm);
   }
   </script>
</head>

<body>
<!--- Set up the Grid --->
<cfform id="CouponForm" name="CouponForm">
<cfgrid name="CouponsGrid"
format="html"
pagesize="10"
striperows="yes"
selectmode="edit"
bind="cfc:coupons.getCoupons({cfgridpage},{cfgridpagesize},{cfgridsortcolumn},{cfgridsortdirection})"
onchange="cfc:coupons.editCoupon({cfgridaction},{cfgridrow},{cfgridchanged})">

<cfgridcolumn name="Couponid" display="false" />
<cfgridcolumn name="SPONSORID" header="Sponsor" width="100"/>
<cfgridcolumn name="COUPON" header="Coupon" width="100"/>
<cfgridcolumn name="EXPIRATIONDATE" header="Exp Date" width="200"/>
</cfgrid>
</cfform>

<!--- use AjaxOnLoad to set the date renderer --->
<cfset ajaxOnLoad("setDateRenderer")>
</body>
</html>

</html>

coupons.cfc

<cfcomponent output="false">

<cfset THIS.dsn="cfbookclub">


<!--- Get art by media type --->
<cffunction name="getCoupons" access="remote" returnType="struct">
   <cfargument name="page" type="numeric" required="yes">
   <cfargument name="pageSize" type="numeric" required="yes">
   <cfargument name="gridsortcolumn" type="string" required="no" default="">
   <cfargument name="gridsortdir" type="string" required="no" default="">
   
   <!--- Local variables --->
   <cfset var data="">

   <!--- Get data --->
   <cfquery name="data" datasource="#THIS.dsn#">
   SELECT COUPONID, SPONSORID, COUPON, EXPIRATIONDATE
   FROM COUPONS
   </cfquery>
   
   <cfset qryFinalQuery = QueryNew("COUPONID, SPONSORID, COUPON, EXPIRATIONDATE","varchar,varchar,varchar,varchar") />
   <cfloop query="data">
      <cfset QueryAddRow(qryFinalQuery) />
      <cfset QuerySetCell(qryFinalQuery, "COUPONID", "#data.COUPONID#") />
      <cfset QuerySetCell(qryFinalQuery, "SPONSORID", "#data.SPONSORID#") />
      <cfset QuerySetCell(qryFinalQuery, "COUPON", "#data.COUPON#") />
      <cfset QuerySetCell(qryFinalQuery, "EXPIRATIONDATE", "#DateFormat(data.EXPIRATIONDATE, 'mm/dd/yyyy')#") />
   </cfloop>
   <!--- And return it as a grid structure --->
<cfreturn QueryConvertForGrid(qryFinalQuery,
ARGUMENTS.page,
ARGUMENTS.pageSize)>


</cffunction>

<!--- Edit an coupon --->
<cffunction name="editCoupon" access="remote">
<cfargument name="gridaction" type="string" required="yes">
<cfargument name="gridrow" type="struct" required="yes">
<cfargument name="gridchanged" type="struct" required="yes">

<!--- Local variables --->
<cfset var colname="">
<cfset var value="">

<!--- Process gridaction --->
<cfswitch expression="#ARGUMENTS.gridaction#">
<!--- Process updates --->
<cfcase value="U">
<!--- Get column name and value --->
<cfset colname=StructKeyList(ARGUMENTS.gridchanged)>
<cfset value=ARGUMENTS.gridchanged[colname]>
<!--- Perform actual update --->
<cfquery datasource="#THIS.dsn#">
UPDATE COUPONS
SET <cfswitch expression="#colname#">
                <cfcase value="EXPIRATIONDATE">#colname# = #createodbcdatetime(value)#</cfcase>
               <cfdefaultcase>#colname# = '#value#'</cfdefaultcase>
            </cfswitch>
         
WHERE COUPONID = #ARGUMENTS.gridrow.COUPONID#
</cfquery>
</cfcase>
<!--- Process deletes --->
<!--- <cfcase value="D">
<!--- Perform actual delete --->
<cfquery datasource="#THIS.dsn#">
DELETE FROM COUPONS
WHERE contactid = #ARGUMENTS.gridrow.COUPONID#
</cfquery>
</cfcase> --->
</cfswitch>
</cffunction>

</cfcomponent>

Click Here to See a Working Example

You will also notice in the getCoupons function in coupons.cfc, that there is a bit of a work around going on there. The issue is that the QueryConvertForGrid function does not mesh well with date fields, it automatically puts them into an uncommon date format that most people wouldn't want to use. I found a post on Gary Gilbert's blog that shows how to resolve this problem by rebuilding the query and forcing the date field to be a varchar field with the formatting you want.

Comments
todd sharp's Gravatar Just curious why you didn't use the Ext dateRenderer?

http://extjs.com/deploy/ext/docs/output/Ext.util.F...
# Posted By todd sharp | 11/27/07 5:58 PM
Scott Bennett's Gravatar @Todd,

Indeed, I tried that first but I couldn't figure out how to get it to work. I had my setDateRenderer function set up like:

setDateRenderer = function(){
      mygrid = ColdFusion.Grid.getGridObject('CouponsGrid');
      cm = mygrid.getColumnModel();
      cm.setRenderer(3, Ext.util.Format.dateRenderer('m/d/Y'));
      mygrid.reconfigure(mygrid.getDataSource(),cm);
   }

But, I kept getting the error "v.dateFormat is not a function".

That's when I decided to build my own function for formatting the date. At first I built it using functions in another dateFormat.js file I have used in the past, but then I realized that the CFIDE folder probably had the EXT date.js file somewhere, so I found it and used that instead.

After your comment I looked at it further, and It turns out that all I really needed to do was add that script tag importing the date.js file, that is why I was geting the "dateFormat is not a function" error.
# Posted By Scott Bennett | 11/27/07 6:16 PM
Scott Bennett's Gravatar I went ahead and modified the JavaScript on the CouponForm.cfm example so it uses the Ext.util.Format.dateRenderer instead of the little function I built, since that is the better way to do it. In case anyone cares, I originally posted the javascript like this:

<script language="JavaScript" type="text/javascript">
   formatDates = function(data,cellmd,record,row,col,store){
if(!data){
return "";
}
if(!(data instanceof Date)){
data = new Date(Date.parse(data));
}
return data.dateFormat("m/d/Y");
}
   setDateRenderer = function(){
      mygrid = ColdFusion.Grid.getGridObject('CouponsGrid');
      cm = mygrid.getColumnModel();
      cm.setRenderer(3, formatDates);
      mygrid.reconfigure(mygrid.getDataSource(),cm);
   }
</script>
# Posted By Scott Bennett | 11/27/07 6:29 PM
Scott Bennett's Gravatar Another thing I just realized is that I could also have used:

<cfajaximport tags="cfinput-datefield">

to import the date.js file instead of:

<script src="/CFIDE/scripts/ajax/ext/package/date.js" type="text/javascript"></script>

but it works either way.
# Posted By Scott Bennett | 11/27/07 6:37 PM
Kevin's Gravatar Scott,
One thing I noticed using an editable grid is that the renderer doesn't seem to affect the format once I click on a cell in the grid to edit it - so that when editing it shows a data as July, 10 2007 00:00:00 but when not editing it shows it as 2007-07-10. Any idea how to change the format for that as well so that I can send the data back as it is shown when not editing? Thanks.
# Posted By Kevin | 12/12/07 1:04 AM
Kevin's Gravatar One other method that may help people out is described at http://www.garyrgilbert.com/blog/index.cfm/2007/7/... - that sends the data in whatever format you want using the CF dateFormat function so it gets to the grid they way you want it.
# Posted By Kevin | 12/12/07 1:27 AM
Scott Bennett's Gravatar @Kevin,

If you read the last paragraph of myblog entry above you will see that I touched on the subject you are referring to and I referenced that same entry from Gary Gilbert's blog.
# Posted By Scott Bennett | 12/12/07 1:26 PM
Michael White's Gravatar has anyone tried displaying both date and time? in the cfc I have something like #DateFormat(data.DateEntered,"mm-dd-yyyy")# #TimeFormat(data.DateEntered)# but the cell renderer ignores all of that. I wonder what the format requirements and syntax is to get the grid to display the way I want
# Posted By Michael White | 1/4/08 11:15 AM
Scott Bennett's Gravatar If you are using my example above you would change the coupon.cfc so that the line:

<cfset QuerySetCell(qryFinalQuery, "EXPIRATIONDATE", "#DateFormat(data.EXPIRATIONDATE, 'mm/dd/yyyy')#") />

would be like:

<cfset QuerySetCell(qryFinalQuery, "EXPIRATIONDATE", "#DateFormat(data.EXPIRATIONDATE,'mm-dd-yyyy')# #TimeFormat(data.EXPIRATIONDATE)#") />

then in CouponForm.cfm you would change the date format strings in the setDateRenderer function and the dataValidator function to use the format 'm/d/Y h:i A' instead of 'm/d/Y'.

For a complete list of format characters read this page:

http://extjs.com/deploy/ext/docs/output/Date.html
# Posted By Scott Bennett | 1/4/08 12:44 PM
Scott Bennett's Gravatar Sorry, the example above does not have a "dataValidator" function. You would only need to modify the date format string for the setDateRenderer function. The "dataValidator" function is in another post I wrote that demonstrates how to create javascript validation for data submitted in a CFGrid.
# Posted By Scott Bennett | 1/4/08 12:57 PM
Michael White's Gravatar Thank you, exactly what I was looking for!
# Posted By Michael White | 1/4/08 1:00 PM
Michael White's Gravatar now that I'm past that problem, I'm getting NaNNaNNaN 12:NaN when the field is null. is there a syntax like if dateentered = null then dateentered = " "
# Posted By Michael White | 1/4/08 3:32 PM
Scott Bennett's Gravatar So just to be clear... The date field in your grid is not requried and you want to allow null values to be entered without throwing any errors, correct?
# Posted By Scott Bennett | 1/4/08 5:02 PM
Michael White's Gravatar Yes, one field is date/time entered and another is date/time approved... you always have date entered, but initially not yet approved
# Posted By Michael White | 1/4/08 6:42 PM
Scott Bennett's Gravatar In the example above you would change the line in the CFC that is setting the query cell for the unrequired datetime column so that it checks to see if there is a value before formatting it for the grid like this:

<cfif trim(data.EXPIRATIONDATE) neq "">
         <cfset QuerySetCell(qryFinalQuery, "EXPIRATIONDATE", "#DateFormat(data.EXPIRATIONDATE,'mm-dd-yyyy')# #TimeFormat(data.EXPIRATIONDATE)#") />
      <cfelse>
         <cfset QuerySetCell(qryFinalQuery, "EXPIRATIONDATE", "") />
      </cfif>
# Posted By Scott Bennett | 1/4/08 7:09 PM
Matt Grimm's Gravatar Why is only the last column (the date field) editable. You can double click and edit it but not the others.. In the grid you have the selectmode set to edit. Just curious if this is intentional or a bug in the grid. I like the idea of only making certain fields editable but didn't know that was possible.

Here's another post of yours that shows the same behavior..

http://www.coldfusionguy.com/ColdFusion/blog/index...
# Posted By Matt Grimm | 1/23/08 5:37 PM
Scott Bennett's Gravatar When I originally wrote it, all the columns were editable, however, someone with a little less maturity than average took it upon themselves to write some profane and indecent things in those fields. So I made them not editable as they were not needed for that examples.
# Posted By Scott Bennett | 1/23/08 5:47 PM
Matt Grimm's Gravatar so how did you make only certain fields editable?
# Posted By Matt Grimm | 1/23/08 5:59 PM
Scott Bennett's Gravatar @Matt,

There is an attribute called "select" in the cfgridcolumn tag, the default is "Yes". If you set it to "No", the field cannot be selected or edited. So I updated the sponsorid and coupon columns to be like this:

<cfgridcolumn name="SPONSORID" header="Sponsor" width="100" select="No"/>
<cfgridcolumn name="COUPON" header="Coupon" width="100" select="No"/>
# Posted By Scott Bennett | 1/23/08 6:13 PM
Matt Grimm's Gravatar nice! Thanks.
# Posted By Matt Grimm | 1/23/08 6:35 PM
frank's Gravatar Have you guys tried to sort the date column with custom rendering?
It doesn't sort the date column properly. It sorts by the first character of the date value like the following.
01/20/2008
02/20/2007
03//20/2007
I tried to cast from cfquery in my function and custom render
by using Ext.util.Format.dateRenderer but no luck. Is there something that I am totally missing here?

Thanks in advance for anyone who figured this out.
# Posted By frank | 5/14/08 7:08 PM
Henry Ho's Gravatar Good news!

I found a way that can bypass the date formatting by queryConvertForGrid(), and also can use Ext.util.Format.dateRenderer()


in MS-SQL:

SELECT convert(varchar,NM.createDate, 1) + ' ' + convert(varchar,NM.createDate, 108) AS Date
FROM XXX

Since this is the format that Ext1.0 date object takes, (see: http://extjs.com/deploy/ext-1.0.1/docs/output/Date... ), we can use Ext.util.Format.dateRenderer() without any additional JS!!!


Below is a more complete example:

<cfsavecontent variable="head">
   <script src="/CFIDE/scripts/ajax/ext/package/date.js" type="text/javascript"></script>
   <script type="text/javascript">
   applyCustomRenderer = function() {
      grid = ColdFusion.Grid.getGridObject('newMeterGrid');
      cm = grid.getColumnModel();

      cm.setRenderer(0, Ext.util.Format.dateRenderer('Y-m-d'));
      // 0 is the first cfgridcolumn

      grid.reconfigure(grid.getDataSource(), cm);
   };
   </script>
</cfsavecontent>

<cfhtmlhead text="#head#">
<cfset ajaxOnLoad("applyCustomRenderer")>
# Posted By Henry Ho | 6/1/08 11:35 AM
 
Home | Blog | Portfolio | Contact | © 2001 - 2007 The ColdFusion Guy - Scott Bennett. All rights reserved.
BlogCFC was created by Raymond Camden. This blog is running version 5.9.