Reacting to grid row selection

This week must be grid week. First we have Bruce Phillips' post on cfgrid last night, and today Todd Sharp posted on it as well. I thought I'd follow the crowd and share a tip as well. How do you do something (anything) when a user clicks on a grid, specifically when using the new HTML cfgrid in ColdFusion 8?

Technically there isn't a attribute that you can add to the grid itself. However, other form fields can easily bind to the grid itself. So if you wanted to build an edit form, for example, that is pretty easy (and again, see Bruce Phillips' post). But what if you wanted more fine grained control? This is where CFAJAXPROXY comes in.

Now if you are like me - you looked at the docs on CJAJAXPROXY and were impressed with how cool it is to be able to create a connection to a CFC. That's amazing, and in my mind, I think it is the most impressive new feature in ColdFusion 8.

But what people may miss (I know I did!) is that CFAJAXPROXY has a whole other... "mode" we shall call it, that lets the tag act in a complete separate manner. In fact, it is so different I'm not quite sure why another tag wasn't made, like CFBIND for example. This other mode takes one main attribute, bind. You can also use a onSuccess and onError attribute. In this mode, the tag simply acts as a listener. So image my grid is named entries. I can bind to it like so:

<cfajaxproxy bind="javascript:noteChange({entries.title})">

All this says is - when the grid changes - call a JavaScript function and pass the value of the Title column. I could also use a CFC or CFM bind as well, but you get the idea. Again, I think CFBIND would be a better name since in this form the tag isn't creating a proxy object for a remote CFC. But that's neither here nor there - lets get back to the original question - how do I react to a grid change?

First you need to figure out what values you want. In my code sample above, I grabbed the title column. If I wanted something different, like ID, I'd do:

<cfajaxproxy bind="javascript:noteChange({entries.id})">

Or I can do both:

<cfajaxproxy bind="javascript:noteChange({entries.title},{entries.id})">

Now for a complete example:

<cfajaxproxy bind="javascript:noteChange({entries.title},{entries.id})">

<script>
function noteChange(title,id) {
   alert(title+' '+id);
}
</script>

<cfquery name="entries" datasource="blogdev">
select   *
from   tblblogentries
limit 0,10
</cfquery>

<cfform name="test">
<cfgrid autowidth="true" name="entries" format="html" query="entries" pageSize="10">
   <cfgridcolumn name="id" display="false">

   <cfgridcolumn name="title" header="Title">
   <cfgridcolumn name="posted" header="Posted">
</cfgrid>
</cfform>

In this example I'm loading up my HTML grid with a query (yes, you don't have to use Ajax for HTML grids). On the top of my template I bound my JavaScript function to the entries grid. Whenever I selected an entry in my grid, an alert will be fired.

I want to point out one last thing. Take a look at the first cfgridcolumn. Why do I have a column that I don't bother displaying? When you specify the columns to show - you are also limiting the data actually stored in the grid. If I didn't have that hidden column there, I wouldn't be able to use the ID column in my bind.

Comments

Yes it has been a gridy week.

Thanks to your help I have got some pretty cool grids working by binding to a cfdiv.

Here is a little demo of one.

http://demo.thinksys.com/cf8/cfgrid/contacts.cfm


Thanks again for all the help.
# Posted By Steve Sequenzia | 8/9/07 5:34 PM
Hey Ray, this works great except for one thing - when the page loads, it fires on the first row as it starts out selected. Is there a way to make it not auto-select the first row when loading a grid? I couldn't find anything about that in the CF8 docs. Thanks!
# Posted By Will | 8/9/07 7:19 PM
To me - that's a feature. :) Try selectOnLoad="false".
# Posted By Raymond Camden | 8/9/07 8:03 PM
Yup... selectOnLoad="false" works. I like it better on also, though.
# Posted By Steve Sequenzia | 8/9/07 8:15 PM
Thanks guys. I agree that it's a great feature that it can auto-select, but when I want clicking on the grid to take you somewhere else, it's not so good :)
# Posted By Will | 8/9/07 9:04 PM
Nice use of pod, Steve. I forget how nice it looks. I'll add that to my code.
# Posted By Raymond Camden | 8/10/07 7:36 AM
Last question on this, I promise. Is there a way to do this based on the specific column a user clicked on versus just the entire row? I'd like to perform different javascript calls based on the column in the row the user selected.
# Posted By Will | 8/10/07 9:00 AM
Ray,

Is their a way to add a image to the data grid?

Your this example is coooooooooooool.

Abul
# Posted By Abul | 8/10/07 10:02 AM
Abul: Yes. You just include it in your data. So for example, I added this to a test to modify an existing query before passing it to my grid:

<cfloop query="getData">
   <cfset querySetCell(getData, "test", "<img src='http://www.coldfusionjedi.com/images/me.jpg'>", currentROw)>
</cfloop>

I'll do a full demo later today.

@Will: I don't think so.
# Posted By Raymond Camden | 8/10/07 11:28 AM
Hey Steve - that demo you have at http://demo.thinksys.com/cf8/cfgrid/contacts.cfm is pretty cool. I'm interested to see the code. If you're up for it, please post the code you created for your cfgrid-cfpod example. (I'm sure some others would like to see what you've done too! :-)
# Posted By Marty McGee | 8/12/07 7:44 PM
@Marty

I just added the code to the bottom of the demo page. Let me know if you have any questions that I can help with.

I also did a demo with two connected grids. Here is a demo of it:

http://demo.thinksys.com/cf8/cfgrid/2grids.cfm
# Posted By Steve Sequenzia | 8/12/07 10:07 PM
@ Steve

SWEET! Thanks for doing this. Just so others can follow along, Steve Sequenzia has offered two code examples for binding data 1) to a cfdiv, and 2) to a second cfgrid on the same page, based on a cfgrid row click (awesome):

1) cfgrid-to-cfdiv example:
http://demo.thinksys.com/cf8/cfgrid/contacts.cfm

2) cfgrid-to-cfgrid example:
http://demo.thinksys.com/cf8/cfgrid/2grids.cfm

Thanks Steve! I'm sure this will be very helpful for readers.
# Posted By Marty McGee | 8/12/07 10:27 PM
Does anyone know how to 'de-select' all rows in the grid using JavaScript? I have a function that I want to run that requires all the rows to be cleared. So far, the only way I can seem to do this is to re-load the grid. That seems like a waste of processing. Any thoughts?
# Posted By Mike Sprague | 8/14/07 1:08 PM
Steve's examples are very cool.
If you do a 'veiw source' on those pages, though, you can see that there are a bunch of .js files callled at the top. Anyone know how much file size overhead this stuff is adding??
# Posted By Tony Garcia | 8/17/07 6:11 AM
All right. I analyzed Steve's first example using the site analyzer at http://www.websiteoptimization.com. Here's how it breaks down:
HTML: 3.1 kb
CSS: 6.7 kb
Javascript: 435 kb!

Wow. All these cool AJAX tricks that you can do with CF8 look cool. But a simple page like this weighing in at almost a half megabyte, you better hope none of your users are still on dialup....
# Posted By Tony Garcia | 8/17/07 12:15 PM
Rey bango made a post about shrinking the CF js files. Try using Dojo's Shrinksafe:

http://alex.dojotoolkit.org/shrinksafe/

I did it for the Spry js files and they all still work perfectly but are a fraction of their original size.

(original blog link to Rey's post : http://www.reybango.com/index.cfm/2007/7/22/Compre...)
# Posted By Will | 8/17/07 2:45 PM
I want to +1 the recommendation on the Spry files. To be fair to the Spry team - Spry isn't officially released yet, and they have always promised to shrink the files when totally done.
# Posted By Raymond Camden | 8/17/07 2:50 PM
I don't want to sound like a naysayer here, but looking at the generated source of Steve's first example, the vast majority of the javascript files called are from the YUI and EXT packages, not Spry. And Rey's post mentioned that only the Spry files are uncompressed. So unless I'm not understanding something here, compressing the Spry files shouldn't make much of a difference.
# Posted By Tony Garcia | 8/17/07 4:47 PM
Hi Ray,

Nice post. However, I think I've found a bug. If you click on a title, the javascript popup displays with the title and the ID just fine. However, if you close the popup window, then click on the same title again, no popup appears. I know this is a minor issue, however, I believe some users will invariably close the window, then want to see the same info again.

How to get around this one?

Thanks,

Peter
# Posted By Peter Swanson | 8/21/07 6:54 PM
Hmm. I tried to specify an event:

<cfajaxproxy bind="javascript:noteChange({entries.title@mousedown},{entries.id@click})">

but it didn't work. Notice I tried both mousedown and click.
# Posted By Raymond Camden | 8/22/07 8:19 AM
Hello Everyone,

I was studying the code, and could not duplicate the examples posted, can you Steve Sequenzia please post a zip of the files, so that I can study the source, along with any one else for the matter?

a studying student,
Camilo
# Posted By Camilo | 9/1/07 9:56 PM
Here is a zip of the examples I did:

http://demo.thinksys.com/cf8-grid-examples.zip

Let me know if I can help.

-Steve
# Posted By Steve Sequenzia | 9/1/07 11:15 PM
Well thanks for the understanding, there was an error
the code could not find the cfc

it is in the same directory, and still error. But I have the code, and this has greatly help me out. thanks for giving me the leg up with this ;-)
# Posted By Camilo | 9/2/07 12:35 AM
Never Mind, I got it... I caught the error..it was the db connection. I set one up and the light came on. Thanks everyone for sharing such great code.

There is a lot that I can learn from from everyone here !
# Posted By Camilo | 9/2/07 1:38 AM
I needed to have a cflayout bind to 2 differents grids. In the cflayout there is the form code to update news in the DB. In the first grid there is a list of last 20 inserted news. Clicking on the grid you can update the news using the form. But I needed also a search form to retrieve old news to update. This search form populate a second grid. To have both grid working on grid change I used the cfajaxproxy as explained by Raymond

<script>
function UpdateFromGrid(gridname,id) {
ColdFusion.navigate('newseditor.cfm?action=update&ID='+id,'CentralAdmin');
</script>

And

<cfajaxproxy bind="javascript:UpdateFromGrid('SearchResults',{SearchResults.id})">
<cfajaxproxy bind="javascript:UpdateFromGrid('LastNews',{LastNews.id})">

Where SearchResults and LastNews are the two cfgrids and CentralAdmin is the cflayout.

But I had the problem of selecting the previous selected row, arised by Peter. This is a problem in particular if your search query returns just one row. You can click on it just once. This other great article by Raymond (http://www.coldfusionjedi.com/index.cfm/2007/8/20/...) suggested my the solution:


<script>
function UpdateFromGrid(gridname,id) {
ColdFusion.navigate('newseditor.cfm?action=update&ID='+id,'CentralAdmin');
mygrid = ColdFusion.Grid.getGridObject(gridname);
sm = mygrid.getSelectionModel()
sm.clearSelections(false);
</script>

Unfortunately this didn't work. The selection was cleared but the onChange event was still not fired when selecting again a previously selected row.

Studying cfgrid.js I think to have finally find the solution:

<script>
function UpdateFromGrid(gridname,id) {
ColdFusion.navigate('newseditor.cfm?action=update&ID='+id,'CentralAdmin');
mygrid = ColdFusion.Grid.getGridObject(gridname);
sm = mygrid.getSelectionModel()
sm.clearSelections(false);
ColdFusion.objectCache[gridname].selectedRow=-1;
</script>

This worked for me. Now I have two different grid controlling a cflayout and you can click on previously clicked row and have the action fired.
# Posted By Matteo Stagi | 9/14/07 7:46 AM
Ray,

Thanks so much for this very useful post. The CF 8 docs are very obtuse on this point; they make it seem like you can just use the cfgrid's onchange event listener. However, onchange doesn't seem to work in this context. Your post has saved the day after about 4-6 hours of searching.

Thanks!

-Neil
# Posted By Neil | 10/8/07 5:19 PM
Does not work when using flash format. Any work around?
# Posted By Michael Skinner | 10/31/07 1:03 PM
Not to be silly - but don't use Flash Forms. Seriously. If you like them- use Flex 2 where you have much more control over them.
# Posted By Raymond Camden | 10/31/07 1:08 PM
I have no choice in the matter
# Posted By Michael Skinner | 10/31/07 1:33 PM
Just trying to create a new tab in a cflayout when a grid item is selected. I managed to get it to create and select the tab but I can't seem to pick up the id that I give to the new tab's source attribute. The id doesn't seem to be in the attributes or url scope (I'm using fusebox).

I have a fuseaction that sets up the cflayout. A default tab has another fuseaction as its source. This tab contains a cfgrid which when a row is click it fires the noteChange function which creates a new tab and uses another fuseaction as the source also passing the id in the source url.

How do I pick up that id within the new tab?

Here's the javascript:

<script>
noteChange = function(orderid) {
var newTab = 'odetail_' + orderid;
var newName = 'Order Details:- ' + orderid;
var oUrl = 'index.cfm?page=admin.orderDetail&orderid=' + orderid;
ColdFusion.Layout.createTab('tabcontainer',newTab,newName,oUrl,{closable:true});
ColdFusion.Layout.selectTab('tabcontainer',newTab);
}
</script>

Also, notice that I had to define the function as noteChange = function() {}

rather than just function noteChange() {}

I read in the docs that this is something to do with the fact that the script is running from within a tab rather than the top level page.
# Posted By Dave Phipps | 12/10/07 11:09 AM
scratch that last post - I had the orderid in the url with &amp; instead of & and changing this seems to have resolved the problem.
# Posted By Dave Phipps | 12/10/07 11:11 AM
I'm working on something similar to steve's second example wherein i want to filter grid2 results based on grid1 selection which works fine except that I WANT ALL RECORDS TO BE DISPLAYED IN GRID2 ON PAGE LOAD. I have selectOnLoad=false (TO DISABLE SELECTION ON LOAD SO I CAN HAVE ALL RECORDS IN GRID2) in grid1 & bind attribute for grid2 has this:
bind="cfc:employeeService.getData({cfgridpage},{cfgridpagesize},
   {cfgridsortcolumn},{cfgridsortdirection},{grid1.FirstName})"

i just don't know whats getting passed in {grid1.FirstName} the first time page loads. The grid is always blank on page load. Only when a row is clicked, results are filtered in grid2.

I've been scratching my head for hours..unable to find whats wrong..pls help someone..
# Posted By sneha | 8/2/08 2:50 PM