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.

TweetBacks
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
Mario  Lago's Gravatar Why no demo ? Idiot...
# Posted By Mario Lago | 8/27/08 10:05 AM
Steve 'Cutter' Blades's Gravatar @Mario -

Thank You Mario, for undervaluing the effort that I put out to share information that people can use. If you had looked around some, you might have noticed that the download link contained all of the sample code, which you could run yourself, or seen the next post on the blog stating that the download had been added. No, you felt it necessary to call me an Idiot within my own space. Thank you so much for letting me know just how much my hard work, on my own time, to help others means to the community. Truly inspirational.
# Posted By Steve 'Cutter' Blades | 8/27/08 10:46 AM
bb's Gravatar Steve, I think that Mario's comment was actually directed at R. Bango not you. At least I "hope" that it was :)
# Posted By bb | 5/15/09 12:41 PM
Fatboy Csaba's Gravatar Thanks for explaining properly how to use Ext.data.XmlReader. You make it simple.
# Posted By Fatboy Csaba | 8/14/09 1:59 AM
Dan O'Keefe's Gravatar Steve, in index.cfm there is a bind on the grid using CFC cfc:controller.RequestFactory which generates an error.
# Posted By Dan O'Keefe | 9/14/09 5:23 PM
Steve 'Cutter' Blades's Gravatar Dan,

Call me Cutter:)

I'll have to go back and look over this code. It's been out there a while, so something might have changed...? Which server version are you using?
# Posted By Steve 'Cutter' Blades | 9/15/09 6:23 AM
Dan O'Keefe's Gravatar Cutter,

CF 8.01 Enterprise. Thanks.
# Posted By Dan O'Keefe | 9/15/09 9:40 AM
Steve 'Cutter' Blades's Gravatar Dan,

Wow! Not sure how this has been missed all this time, but my original example contained another folder with a CFC for getting data from one of the CF sample (derby) datasources. I guess I forgot to include it in the zip, which is now way long gone.

The importance to the examples is really in understanding how, through JavaScript, you can create custom renderers for your columns, as well as a general understanding of working with an event driven paradigm.

If you have any questions, drop me a line.
# Posted By Steve 'Cutter' Blades | 9/15/09 3:47 PM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.3.006. Contact Blog Owner. Layout inspired by bluerobot.com., with some JQuery thrown in for fun.