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.
Archived 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/cf...
Thanks again for all the help.
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!
To me - that's a feature. :) Try selectOnLoad="false".
Yup... selectOnLoad="false" works. I like it better on also, though.
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 :)
Nice use of pod, Steve. I forget how nice it looks. I'll add that to my code.
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.
Ray,
Is their a way to add a image to the data grid?
Your this example is coooooooooooool.
Abul
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.c...", currentROw)>
</cfloop>
I'll do a full demo later today.
@Will: I don't think so.
Hey Steve - that demo you have at http://demo.thinksys.com/cf... 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! :-)
@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/cf...
@ 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/cf...
2) cfgrid-to-cfgrid example:
http://demo.thinksys.com/cf...
Thanks Steve! I'm sure this will be very helpful for readers.
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?
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??
All right. I analyzed Steve's first example using the site analyzer at http://www.websiteoptimizat.... 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....
Rey bango made a post about shrinking the CF js files. Try using Dojo's Shrinksafe:
http://alex.dojotoolkit.org...
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/ind...
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.
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.
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
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.
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
Here is a zip of the examples I did:
http://demo.thinksys.com/cf...
Let me know if I can help.
-Steve
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 ;-)
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 !
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.c... 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.
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
Does not work when using flash format. Any work around?
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.
I have no choice in the matter
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.
scratch that last post - I had the orderid in the url with & instead of & and changing this seems to have resolved the problem.
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..
I've successfully implemented this on a web site
http://www.rgpc.ca/resource/
Grid click opens a cfwindow
Click on the [X] or a close window hyperlink calls a javascript which runs
ColdFusion.Window.destroy('window_name',true);
BUT, if the user then clicks on the same row in the grid, nothing happens. They have to click on a different grid row to open (create) another window.
Any ideas how to fix?
<script language="javascript">
function noteChange(id) {
ColdFusion.Window.create('resource_detail', 'Resource Details', 'http://www.rgpc.ca/resource... + id, {modal:false, width:<cfoutput>#cfwindow_width#</cfoutput>, height:<cfoutput>#cfwindow_height#</cfoutput>, closable:true, x:400, y:100});
ColdFusion.Window.onHide('resource_detail', destroy_window);
}
function destroy_window() {
ColdFusion.Window.destroy('resource_detail',true);
</script>
any help much appreciated!
Cheers,
Matts
I've been looking around some more and it seems some people have had some success with
ColdFusion.objectCache['resource_list'].selectedRow=-1;
where 'resource_list' is the name of the cfgrid
I've added this in various places in my javascript -- no js errors are posted, but also no luck!
Matts
Thanks to Matteo Stagi from above (a couple years ago)... there is some javascript that FINALLY solves this issue of not being able to 're-click' the same grid row...
mygrid = ColdFusion.Grid.getGridObject('resource_list');
sm = mygrid.getSelectionModel()
sm.clearSelections(false); ColdFusion.objectCache['resource_list'].selectedRow=-1;
Hope this helps someone out there!
I couldn't get ajaxproxy to work in my case. I went with this alternative: http://www.danvega.org/blog...
As always I had a problem and you came up with the solution. Thanks once more!
I am just dipping my toe into the CF Ajax world and am loving it, but ran into some issues, where I am stumped. I have a cfgrid that is pulling a query from a cfc. I got this to work with no problems. Now I am attempting to utilize cfwindow for detail information. So, when an end user clicks on a row in the grid, a cfwindow pops up with more detailed information. It works great the first time. After I close the window and click another row, the ID is null. Thoughts?
Here is my code...
function showDetail(id) {
alert(id);
var windowID = '_detail'+id;
ColdFusion.Window.create(windowID,'Order Details','details.cfm?orderDetails='+id,{x:100,y:100,height:150,width:500,modal:false,closable:true,draggable:false,resizable:false,center:false,refreshOnShow:true}); ColdFusion.objectCache[getOrders].selectedRow=-1;
}
FYI, the cfwindow, simply contains code that pulls a query based upon the passed url parameter to populate a cfgrid within the cfwindow.
Ray...disregard previous post. I found issue. To "save time", I copied the code for the grid from the parent page to the cfwindow. I forgot to change the name. Duh!
Keep using the force!
Well this does not work if the form format is flash .
Correct. I do not recommend using Flash Forms anymore. Just use Flex. Newer - better - and free.
using the code in this post, how can I move records from a cfgrid to another emoty grid when selected?
What's an emoty grid?
Sorry, I meant empty...
You would need to look at Ext's API for Grids. CF gives you access to get the core grid object. So you would need to see how one adds rows to grids dynamically.
Hello,
I'd like to put a button inside of a grid toolbar and make a bind to that button with cfajaxproxy.
var tbar = ColdFusion.Grid.getTopToolbar("artists");
//var tbar = grid.getTopToolbar();
tbar.addButton({text:"Delete"
, tooltip:"Aplica filtrul pe randurile selectate."
, id:"deletebutton"
, name:"deletebutton"
, iconCls:"icon-table-save"
, disabled: false
/*, handler:onApplyFilter */
});
tbar.addSeparator();
//grid.showTopToolbar();
ColdFusion.Grid.showTopToolbar("artists");
How can I do this?
If I use
<cfajaxproxy bind="javascript:noteChange({artists.deletebutton@click},{artists.id@none})">
the script fires when I click a row (and the corespondent to the artists.deletebutton@click is undefined), but not when I click that button.
Thank you
Does anyone has a working example of binding the cfgrid to the cfdiv? The links posted by Steve Sequenzia are now broken :(.
Thanks!
Got it. It's simple as
<cfdiv bind="{gridname.gridcolumnname}" ID="myDiv" "/> .