CF8 Ajax Grid: Renderers and Events

So, I was doing a real quick, down and dirty form and results app for something internal. Way temporary, with little scale-out, I wrote a form and processor, then used the CF8 DataGrid for the results display. Problem was, two of the fields were textareas that could contain a lot of info, so I needed a quick way to show and expanded details set. Now, had I been using ExpanderRow plugin, but this was just quick implementation prototyping type stuff.

What I needed was a column of icons that I could then link to a CFWindow with the total display. Now, I have to use a Cell Renderer to place the image in the empty column, but first I need the column.

<cfgridcolumn name="Details" header="" width="25" display="true" />

After that, I create a basic Cell Renderer:

setDetailButtonRenderer = function(grid,cm,col){
      cm.setRenderer(col,function(value,p,r,ind){
         var retVal = "<img src='/resources/images/icons/book_link.gif' width='16' height='16' alt='Details' />";
         return retVal;}
      });
      grid.reconfigure(grid.getDataSource(),cm);
   }

This didn't entirely work out, as it placed the image in every row, even if there wasn't a record. So, time to improvise. I adjust to see if there's value for a cell in this row's 'record', to determine whether I need the image.

setDetailButtonRenderer = function(grid,cm,col){
      cm.setRenderer(col,function(value,p,r,ind){
         var ds = grid.getDataSource();
         var theRecord = ds.getAt(ind);
         if(theRecord.get('TS') != null){
            var retVal = "<img src='/resources/images/icons/book_link.gif' width='16' height='16' alt='Details' />";
            return retVal;
         }
      });
      grid.reconfigure(grid.getDataSource(),cm);
   }

   function showRecWin(){
   ColdFusion.Window.show('winDetails');
}

Alright, to call the renderer into play I have an init method that is fired by the CF ajaxOnLoad() method.

init = function(){
      var repGrid = ColdFusion.Grid.getGridObject('reportsGrid');
      var repCM = repGrid.getColumnModel();

      setDetailButtonRenderer(repGrid,repCM,8);
   }

Now we're halfway there. Next I need to get a 'click' on the image cell. You do this by accessing the underlying Ext functions of the Grid object itself, for which you already have a reference (repGrid).

init = function(){
      var repGrid = ColdFusion.Grid.getGridObject('reportsGrid');
      var repCM = repGrid.getColumnModel();

      setDetailButtonRenderer(repGrid,repCM,8);

      repGrid.on('cellclick',function(grid,rowIndex,columnIndex,e){
         if(columnIndex==8){
            
         }
      });
   }

We are configuring an on cellclick function here, which is really a listener on the row itself. We further narrow it to only perform action if the column that the cursor was in 'on click' was our Details column, which is the 9th column of our grid, including hidden columns (remember that this uses a JavaScript array, which starts with zero, so the column you reference is always column count minus one).

Next thing we need is a quick modal pop-up for our 'Details.' CFWindow makes a great candidate for this.

<cfwindow name="winDetails" title="Details" draggable="false" resizable="false" initShow="false" height="600" width="600" />

It's invisible when initialized, because we only want to show it 'on click'. We need a quick method for 'showing' the window.

function showRecWin(){
   ColdFusion.Window.show('winDetails');
}

We can now reference this in our 'on click' function.

repGrid.on('cellclick',function(grid,rowIndex,columnIndex,e){
      if(columnIndex==8){
         showRecWin();
      }
   });

OK, we get our window, but now we need some data. Now, I could do an ajax call for the data, but it's already in my cell. It's just too long to easily display in the grid. Rather than do another server call, I'll just query the grid's Data.Store for the information.

repGrid.on('cellclick',function(grid,rowIndex,columnIndex,e){
      if(columnIndex==8){
         showRecWin();
         // This empties out any previously displayed content          document.getElementById("winDetails_body").innerHTML = "";
         var ds = grid.getDataSource();
         var theRecord = ds.getAt(rowIndex);
         var valPurpose = theRecord.get('FEATUREPURPOSE');
         var valFunction = theRecord.get('FEATUREFUNCTION');
         document.getElementById("winDetails_body").innerHTML = "<b>Purpose:</b><br />" + valPurpose + "<br /><br /><b>Function:</b><br />" + valFunction;
      }
   });

Really simple, as long as you remember that ColdFusion's creation of the grid's ColumnModel will uppercase all of your cfgridcolumn's name attributes.

That's it. Really doesn't take a whole lot. A little digging in the documentation for the 1.1.1 version of the ExtJS library will give you a ton of information.

Comments
Rey Bango's Gravatar What no demo?!?!!? Bah! Slacker!
# Posted By Rey Bango | 11/30/07 5:20 PM
dave's Gravatar Hi.. Would be great to see a demo of this. I am trying to wrap my brain around this stuff and a visual would help.

Thanks for the post!
# Posted By dave | 12/10/07 3:14 PM
Dave's Gravatar note this helpful tip... you can also code a javascript:function(x) in your SQL that will give you a way to fire an onClick on an image from the grid. example in SQL: ( watch the wrap )

SELECT
'<a href="javascript:showOrderDetail(' +CAST(ROWID AS varchar(5))+ ');"><img src="http://mysite.com/images/button_Open.gif"; width="40" height="15" border="0"></a>' AS CLICK_IMG      
      FROM
         ORDERS
# Posted By Dave | 12/14/07 8:47 PM
Cutter's Gravatar @Dave - That's true, you technically could do it that way. But, if you look deeper in the post, you'll notice that no data is in the Details column. In fact, it itsn't tied to SQL output at all, other than the fact that a cell appears adjacent to a 'record'.

I could have wrapped an 'onclick' anchor around the image in the renderer just as easily, but we lose out on two separate concepts here. The first is the purpose of the post, to show how to do custom renderers and how to add event listeners. The second one gets a little more grey area, to maintain unobtrusive JavaScript. Sure, the grid is rendered by JavaScript to begin with, and the renderer is done by javascript (and all the js is directly in the page anyway, since this implementation is through the CF 8 tags).

But, that is part of the point. It's rendered, whether it was written inline or not. And best practice dictates that JavaScript code should remain unobtrusive, so we assign the event listener, within an external JS file. I know it doesn't make any sense, especially when dealing with Ajax components, but you try to walk the lines of standards as much as possible, vearing when necessary. The usage of 'javascript:method()' within an anchor tag (or any tag), would be considered obtrusive, and should be avoided. Getting out of old, bad habits now will help all of us in the long run.
# Posted By Cutter | 12/15/07 8:46 AM
Mischa's Gravatar Hi Steve, thank you for this example and your explanation! Do I understand correctly that init will fire once? Is there a way to fire init or any other function when the user pages through the grid using the page forward button?
Thanks!
Mischa.
# Posted By Mischa | 1/8/08 3:04 PM
Cutter's Gravatar Mischa,

In answer to your question:

http://blog.cutterscrossing.com/index.cfm/2008/1/9...
# Posted By Cutter | 1/9/08 4:20 PM
todd sharp's Gravatar All good stuff Cutter, but I might suggest using CF's built in binding (or ColdFusion.getElementValue()) inside your cfwindow? Would be a bit easier then querying the data store IMO...
# Posted By todd sharp | 3/4/08 9:05 AM
Steve 'Cutter' Blades's Gravatar @Todd - I'll have to look into that. I would imagine that, under the hood, they are basically the same thing, as the ColdFusion JS namespace is mostly a wrapper collection around the Ext library, but it may be a better implementation. Thanks for the pointer, I'll research it a little.
# Posted By Steve 'Cutter' Blades | 3/4/08 9:14 AM
j's Gravatar I noticed that the dates in your XML where in that ISO format, how did you get it to render properly in the grid?
# Posted By j | 3/23/08 6:41 AM
BlogCFC v. 5.8.001 was created by Raymond Camden. Layout inspired by bluerobot.com., with some JQuery thrown in for fun.