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.

view plain print about
1<cfgridcolumn name="Details" header="" width="25" display="true" />

After that, I create a basic Cell Renderer:

view plain print about
1setDetailButtonRenderer = function(grid,cm,col){
2        cm.setRenderer(col,function(value,p,r,ind){
3            var retVal = "<img src='/resources/images/icons/book_link.gif' width='16' height='16' alt='Details' />";
4            return retVal;}
5        });
6        grid.reconfigure(grid.getDataSource(),cm);
7    }

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.

view plain print about
1setDetailButtonRenderer = function(grid,cm,col){
2        cm.setRenderer(col,function(value,p,r,ind){
3            var ds = grid.getDataSource();
4            var theRecord = ds.getAt(ind);
5            if(theRecord.get('TS') != null){
6                var retVal = "<img src='/resources/images/icons/book_link.gif' width='16' height='16' alt='Details' />";
7                return retVal;
8            }
9        });
10        grid.reconfigure(grid.getDataSource(),cm);
11    }
12
13    function showRecWin(){
14     ColdFusion.Window.show('winDetails');
15 }

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

view plain print about
1init = function(){
2        var repGrid = ColdFusion.Grid.getGridObject('reportsGrid');
3        var repCM = repGrid.getColumnModel();
4
5        setDetailButtonRenderer(repGrid,repCM,8);
6    }

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).

view plain print about
1init = function(){
2        var repGrid = ColdFusion.Grid.getGridObject('reportsGrid');
3        var repCM = repGrid.getColumnModel();
4
5        setDetailButtonRenderer(repGrid,repCM,8);
6
7        repGrid.on('cellclick',function(grid,rowIndex,columnIndex,e){
8            if(columnIndex==8){
9                
10            }
11        });
12    }

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.

view plain print about
1<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.

view plain print about
1function showRecWin(){
2     ColdFusion.Window.show('winDetails');
3 }

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

view plain print about
1repGrid.on('cellclick',function(grid,rowIndex,columnIndex,e){
2        if(columnIndex==8){
3            showRecWin();
4        }
5    });

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.

view plain print about
1repGrid.on('cellclick',function(grid,rowIndex,columnIndex,e){
2        if(columnIndex==8){
3            showRecWin();
4            // This empties out any previously displayed content
5            document.getElementById("winDetails_body").innerHTML = "";
6            var ds = grid.getDataSource();
7            var theRecord = ds.getAt(rowIndex);
8            var valPurpose = theRecord.get('FEATUREPURPOSE');
9            var valFunction = theRecord.get('FEATUREFUNCTION');
10            document.getElementById("winDetails_body").innerHTML = "<b>Purpose:</b><br />" + valPurpose + "<br /><br /><b>Function:</b><br />" + valFunction;
11        }
12    });

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.

Enter The Holidays

Happy Thanksgiving to everyone, and welcome to the holiday season. My mother is in town now, and my family and I just completed our move to our new apartment. Great place, terrific neighborhood, excellent schools...really loving it, now that we're getting settled. Once we put the pictures on the walls and put the moving tubs into storage we'll be golden. And I am loving the telecom company here, with fiber to every unit we now have great digital cable and a 15MB internet connection. Man, it is fast!

Next week I'm going to try and re-record my UG preso on CF8's Ajax Components And Beyond. The original preso went very well, but we had some glitches in the recording process. I hope to get that done and on UGTV by the end of next week. I am working on extending some RIAForge components for working with the Google Maps API. Hoping to make some of that publicly available in the very near future.

And, since we're entering the season of giving, I encourage everyone in the ColdFusion community to contribute to some open source project. CFCommerce is trying to get into the groove, there are hundreds of items on RIAForge, and any of the frameworks would probably appreciate a helping hand.

Happy Holidays to all!

ColdFusion OO Architecture: Get Out Of The Box

For the past two days there has been a very interesting thread being discussed on the Model-Glue mailing list. You have to read through the first few messages in the thread before you really start getting into the meat of the discussion, with some great comments from Sean Corfield and Peter Bell.

It goes back to the ongoing Design Patterns Debate, making it's way around the ColdFusion community. Many have adopted the Table Row Pattern, used by popular ORMs, almost as a standard for development. But is this the right way to go with ColdFusion? Are we writing too much code to accomplish simple tasks?

Sean takes some responsibility for this thought process, believing that some of this has stemmed from his Mach-II Development Guidelines doc while at Adobe. While that may be somewhat true, I think that it probably stems more from the fact that OO is still fairly new to the ColdFusion world. While we've been capable of writing OO code since the introduction of CFCs in 6.1, adoption of the concept has been slow, and only truly picked up major steam over the last two years or more.

In the thread, Sean knocks on the large adoption of the 5:1 business object concept. He doesn't state that it's completely wrong, only that it may be overkill in most cases, and should not be the end-all-be-all. While the Bean-DAO-Gateway paradigm may be great for simple CRUD type operations, and simple table fillers, it's not well suited to complex objects. A Factory approach may be a better option. The primary point is, there is No One Way, and that we shouldn't pigeon hole ourselves into design patterns that are primarily designed for Java, when ColdFusion (being typeless) has more in common with languages like Ruby, Python or Groovy.

There is no One Way, or even Wrong Way, and maybe it's time for all of us to begin thinking outside the box again. We can build great, rapid, OO applications, if we just start doing it.

I've paraphrased some stuff here, so if I've gotten someone's comments wrong, or completely mixed up, I apologize now and please feel free to correct me.