I was working on a project yesterday, and I was using an editable CFGrid to manage a list of data. On my form I wanted to create a button with an onClick event that would take the data from the selected row in my CFGrid , and then do some processing and update that row on the CFGrid and in the database automatically. To do this I needed some way to update the data in a specific cell on the grid,
but there was no obvious way to do that.
I consulted the Adobe docs and they provide us with a few different grid related functions. Originally I had thought about just writing a function that updates the data through a cfc call then calls ColdFusion.Grid.refresh, which Manually refreshes a displayed grid. I couldn't use this however because when you call it the whole grid page gets refreshed and you loose the row you have selected.
I determined at this point I was going to have to use ColdFusion.Grid.getGridObject, which Gets the underlying Ext JS - JavaScript Library object for the specified HTML format CFGrid control. Then I began a long and tedious process of trying to figure out how to use yet another giant JavaScript Library that I am completely unfamiliar with. The ext documentation was a little bit of a help, but ultimately it came down to me just fiddling with it for several hours until it clicked in my head.
Here is a working sample of what I am talking about. This is a simplified version of what I had to do for my real project but it will give you the important part. This is a little contacts manager, and if you select a row and click the "Toggle Cool Factor" button then the selected rows "cool" flag will be changed.
Now for the example code. This code is pretty much like all the other blog entries you see out there by everyone that tell you how to create a cfgrid that is bound to a CFC.
You start with a CFC like with a Get function and an Edit function like:
<cfcomponent output="false">
<cfset THIS.dsn="yourdatabasename">
<!--- Get contacts --->
<cffunction name="getcontacts" access="remote" returntype="struct">
<cfargument name="page" type="numeric" required="yes">
<cfargument name="pageSize" type="numeric" required="yes">
<cfargument name="gridsortcolumn" type="string" required="no" default="">
<cfargument name="gridsortdir" type="string" required="no" default="">
<!--- Local variables --->
<cfset var contacts="">
<!--- Get data --->
<cfquery name="contacts" datasource="#THIS.dsn#">
SELECT contactid, lastname, firstname, email, cool
FROM contacts
<cfif ARGUMENTS.gridsortcolumn NEQ ""
and ARGUMENTS.gridsortdir NEQ "">
ORDER BY #ARGUMENTS.gridsortcolumn# #ARGUMENTS.gridsortdir#
</cfif>
</cfquery>
<!--- And return it as a grid structure --->
<cfreturn QueryConvertForGrid(contacts,
ARGUMENTS.page,
ARGUMENTS.pageSize)>
</cffunction>
<!--- Edit an contact --->
<cffunction name="editcontact" access="remote">
<cfargument name="gridaction" type="string" required="yes">
<cfargument name="gridrow" type="struct" required="yes">
<cfargument name="gridchanged" type="struct" required="yes">
<!--- Local variables --->
<cfset var colname="">
<cfset var value="">
<!--- Process gridaction --->
<cfswitch expression="#ARGUMENTS.gridaction#">
<!--- Process updates --->
<cfcase value="U">
<!--- Get column name and value --->
<cfset colname=StructKeyList(ARGUMENTS.gridchanged)>
<cfset value=ARGUMENTS.gridchanged[colname]>
<!--- Perform actual update --->
<cfquery datasource="#THIS.dsn#">
UPDATE contacts
SET #colname# = <cfif colname eq "cool"><cfif yesnoformat(value) eq "yes">1<cfelse>0</cfif><cfelse>'#value#'</cfif>
WHERE contactid = #ARGUMENTS.gridrow.contactid#
</cfquery>
</cfcase>
</cfswitch>
</cffunction>
</cfcomponent>
Then you create a page with a CFGrid , and javascript to interact with the CFGrid object like this:
<cfajaxproxy cfc="contacts" jsclassname="CFCs.contacts">
<script language="JavaScript">
toggleCool = function(){
currCool = ColdFusion.getElementValue("contactsGrid","contactForm","Cool");
//if only there were a ColdFusion.setElementValue()
//then this would be soooo much easier
//instead we do this:
//create an instance of the cfc I imported above via cfajaxproxy (coolest tag ever!)
var contactsObj = new CFCs.contacts;
//set error handler function
contactsObj.setErrorHandler(errorHandler);
//get the gridobject
var myGrid = ColdFusion.Grid.getGridObject("contactsGrid");
//get the selected row
var row = myGrid.dataSource.data.items[getGridIndexById(myGrid,myGrid.getSelections()[0].id)].data;
// now that I have the selected row, I can access the columns buy using the
// name I assigned the cfgridcolumn in uppercase
if (row.COOL == "1"){
row.COOL = "0";
}else{
row.COOL = "1";
}
// Ihave updated the value of my cool row in the grid datasource now I need to
// update the real database. to do this I call the editcontact function in my
// CFC just like the cfgrid would have
contactsObj.editcontact('U',row,{COOL:row.COOL});
// Then I call the myGrid.view.refreshRow function which reads from the grid data and
// refreshes what is displayed in the specified row
myGrid.view.refreshRow(getGridIndexById(myGrid,myGrid.getSelections()[0].id));
}
// I made a little function here to get the selected rows index by the id
getGridIndexById = function(thisGrid,id){
for(var i=0,_8=thisGrid.dataSource.data.items.length;i<_8;i++){
if(thisGrid.dataSource.data.items[i].id==id){return i;}
}
return -1;
}
//basic error handler
errorHandler = function(statusCode,statusMsg) {
alert(statusCode+': '+statusMsg)
}
</script>
<!--- here is a button with an onclick event to change if they are cool or not --->
<input type="Button" value="Toggle Cool Factor" onclick="toggleCool();">
<cfform id="contactForm" name="contactForm">
<cfgrid name="contactsGrid"
format="html"
pagesize="10"
striperows="yes"
selectmode="edit"
bind="cfc:contacts.getcontacts({cfgridpage},{cfgridpagesize},{cfgridsortcolumn},{cfgridsortdirection})"
onchange="cfc:contacts.editcontact({cfgridaction},{cfgridrow},{cfgridchanged})">
<cfgridcolumn name="contactid" display="false" />
<cfgridcolumn name="lastname" header="Last Name" width="100"/>
<cfgridcolumn name="firstname" header="First Name" width="100"/>
<cfgridcolumn name="email" header="E-Mail" width="200"/>
<cfgridcolumn name="cool" header="Cool Person" width="200"/>
</cfgrid>
</cfform>
And there you have it! Enjoy.
P.s. like the working example!
Check out this article from Todd Sharp:
http://cfsilence.com/blog/client/index.cfm/2006/6/...
ColdFusion.Grid.getGridObject('contactsGrid').getSelectionModel().singleSelect = false;) ? For example if you want to delete multiple records at one time?
You have to override your grid's view to accomplish this. Here's an example:
<style type="text/css">
.red-row{ background-color: #F5C0C0 !important; }
.yellow-row{ background-color: #FBF8BF !important; }
.green-row{ background-color: #99CC99 !important; }
</style>
....
Ext.override(Ext.grid.GridView, {
getRowClass : function (row, index) {
var cls = '';
var data = row.data;
//Set backgroud color depending on data returned
switch (data.STATE) {
//Yellow bg
case '' :
cls = 'yellow-row'
break;
case 'CA' :
cls = 'yellow-row'
break;
//Green bg
case 'NY' :
cls = 'green-row'
break;
//Red bg
case 'DC' :
cls = 'red-row'
break;
}
return cls;
}
});
To accomplish this you'll need to use the getSelections() function to access the array of selected rows.
I needed to put the values into a single sting. To accomplish this, I used the join() function:
var grid = ColdFusion.Grid.getGridObject("contactsGrid");
var selRecs = grid.getSelectionModel().getSelections();
var sm = grid.getSelectionModel();
var ds = grid.getDataSource();
var selnum = selRecs.length;
var arrSelTemp =new Array(selnum);
var len=selRecs.length;
var str="";
for(var i=0; i<len;i++){
var id = selRecs[i].id;
arrSelTemp[i]= selRecs[i].get("CONTACTID");
//use join() to convert array to a string
str = arrSelTemp.join();
alert("Selected contact ids: " + str);
};
Been working through your example as it is best I've found to help me do what I wan to do. But I keep having a problem with the getGridIndexById function. When the function is called from the var row statement, I get thisGrid.dataSource is undefined. I am very new at using these types of function
and this could be just a newbe issue. Any suggestions? (all script shown below)
<cfajaxproxy cfc="SAM_CTV" jsclassname="dataproxy">
<html>
<head>
<script type="text/javascript">
// create a new JS proxy object for the CFC
var dataproxy = new dataproxy();
dataproxy.setCallbackHandler(handleResult);
dataproxy.setErrorHandler(errorHandler);
function handleResult(response)
{
alert(response);
}
// add Button to Toolbar
function init(){
grid = ColdFusion.Grid.getGridObject("getNASRAC");
var gridHead = grid.getView().getHeaderPanel(true);
var tbar = new Ext.Toolbar(gridHead);
tbar.addButton({text:"Update Channel", handler:onChange });
}
function onChange(button,event){
// get Gridobject
var myGrid = ColdFusion.Grid.getGridObject("getNASRAC");
// Get Selected Row
var row = myGrid.dataSource.data.items[getGridIndexById(myGrid.getSelections()[0].id)].data;
// set variables for window output
wKey = row.Hits_Key;
wTier = row.Hits_Tier;
wSource = row.Hits_SourceID;
wSrvType = row.Hits_ServiceProviderType;
wSymbol = row.Hits_Symbol;
wDesc = row.Hits_Name;
ColdFusion.Window.show('chgNASRACChannel');
}
function chgChannelData()
{
//send data to CFC to add artist, the result will be handled by handleResult function above
var f = document.frmchgChannelData;
dataproxy.chgChannelData(
f.txtKey.value
,f.txtTier.value
,f.txtSource.value
,f.txtSymbol.value
,f.txtDescription.value
);
ColdFusion.Window.hide('chgNASRACChannel');
grid.refresh();
}
// create getGridIndexById Function
function getGridIndexById(thisGrid,id){
for(var i=0,_8=thisGrid.dataSource.data.items.length;i<_8;i++){
if(thisGrid.dataSource.data.items[i].id==id){return i;}
}
return -1;
}
//basic error handler
function errorHandler(statusCode,statusMsg) {
alert(statusCode+': '+statusMsg)
}
</script>
Here is my suggested changes using the getSelections(), which should help you get to the data in a column in a selected row a lot easier:
<cfajaxproxy cfc="SAM_CTV" jsclassname="dataproxy">
<html>
<head>
<script type="text/javascript">
// create a new JS proxy object for the CFC
var dataproxy = new dataproxy();
dataproxy.setCallbackHandler(handleResult);
dataproxy.setErrorHandler(errorHandler);
function handleResult(response)
{
alert(response);
}
// add Button to Toolbar
function init(){
grid = ColdFusion.Grid.getGridObject("getNASRAC");
var gridHead = grid.getView().getHeaderPanel(true);
var tbar = new Ext.Toolbar(gridHead);
tbar.addButton({text:"Update Channel", handler:onChange });
}
function onChange(button,event){
// get Gridobject
var myGrid = ColdFusion.Grid.getGridObject("getNASRAC");
// Get Selected Row
//var row = myGrid.dataSource.data.items[getGridIndexById(myGrid.getSelections()[0].id)].data;
var row = myGrid.getSelections();
// set variables for window output
wKey = row.[0].data.Hits_Key;
wTier = row.[0].data.Hits_Tier;
wSource = row.[0].data.Hits_SourceID;
wSrvType = row.[0].data.Hits_ServiceProviderType;
wSymbol = row.[0].data.Hits_Symbol;
wDesc = row.[0].data.Hits_Name;
ColdFusion.Window.show('chgNASRACChannel');
}
function chgChannelData()
{
//send data to CFC to add artist, the result will be handled by handleResult function above
var f = document.frmchgChannelData;
dataproxy.chgChannelData(
f.txtKey.value
,f.txtTier.value
,f.txtSource.value
,f.txtSymbol.value
,f.txtDescription.value
);
ColdFusion.Window.hide('chgNASRACChannel');
grid.refresh();
}
// create getGridIndexById Function
/*function getGridIndexById(thisGrid,id){
for(var i=0,_8=thisGrid.dataSource.data.items.length;i<_8;i++){
if(thisGrid.dataSource.data.items[i].id==id){return i;}
}
return -1;
}*/
//basic error handler
function errorHandler(statusCode,statusMsg) {
alert(statusCode+': '+statusMsg)
}
Many, many thanks!
white if the bgcolor=red otherwise, the textcolor should be set to black.
I've tried the following with no success. It only works if I hard-code the color.
<cfgridcolumn name="MyCol" header="My Column" bgcolor="MyCol">
<cfgridcolumn name="MyCol" header="My Column" bgcolor="CX">
<cfgridcolumn name="MyCol" header="My Column" bgcolor="(CX EQ Green?green:yellow)">
You have to create an override for this. Take a look a few comments up and you will see a comment from Calvert on how to do this. http://www.coldfusionguy.com/ColdFusion/blog/index...
so, I am trying to use this code to help me with my issue, or my 2 issues. First, I have a cfgird with checkboxes, that seems to make it harder.
I have a submit button, where I need to verify at least one row has a checked checkbox onSubmit.
I have a clear all button that needs to find and uncheck all the checkboxes that may be checked.
I copied the example above to a cf page and tried to run it, and I get the datasource.data is null error just like mike.
how is the datasource.data called/what is it?
Any ideas about how to do what I need to do?
thanks for any help
Dan
I used what you said about Caps, and it worked.
So, would you have any idea about how to help me check all the checkboxes in each row, and also select each row?
I also have to figure out how to run a script off of a checkbox...if I check a row, it selects that row...so if I check 3 rows, those 3 rows are selected.
any help would be great...
Are you on CF8 or CF9?
Anyone have any ideas about my issues? I am struggling to figure out EXTJS and how to get it to work with Checkboxes in the grid. If the user checks a checkbox, that line needs to be selected...
I know how to do multiple selects with the shift or ctrl keys, but not trigger off a checkbox. I figure that is a listener, but I don't have any idea how to write one. I looked at the examples posted above, but I really don't
understand them.
Any help would be really appreciated.
Dan
ext3 uses getStore() now not getDataSource() + some other changes.
I implemented in CF 8:
var row = myGrid.dataSource.data.items[getGridIndexById(myGrid,myGrid.getSelections()[0].id)].data;
if (row.ADDED == "N"){
row.ADDED = " Y";
}else{
row.ADDED = "N";
}
And now in CF9 have:
var row = myGrid.getSelectionModel().getSelected();
if (row.data.ADDED == "N"){
row.data.ADDED = " Y";
}else{
row.data.ADDED = "N";
}
<script>
function formOnLoad(){
ColdFusion.Grid.getGridObject('entries2').getSelectionModel().singleSelect = false;
myGrid = ColdFusion.Grid.getGridObject('entries2')
var row = myGrid.getSelectionModel().getSelected();
if (row.data.chkbox == "N"){
row.data.chkbox = " Y"; alert("hi1");
}else{ alert("hi2");
row.data.chkbox = "N";
}
}
</script>
</head>
<body>
<cfset t = queryNew('id,name,chkbox','integer,varchar,bit') />
<cfset queryaddrow(t,1) />
<cfset querysetcell(t,'id',1) />
<cfset querysetcell(t,'name','sean') />
<cfset querysetcell(t,'chkbox',0) />
<cfset queryaddrow(t,1) />
<cfset querysetcell(t,'id',2) />
<cfset querysetcell(t,'name','phillip') />
<cfset querysetcell(t,'chkbox',0) />
<cfset queryaddrow(t,1) />
<cfset querysetcell(t,'id',3) />
<cfset querysetcell(t,'name','steve') />
<cfset querysetcell(t,'chkbox',0) />
<cfdump var="#t#">
<div>
<cfform name="test2">
<cfgrid name="entries2" format="html" width="600" query="t" height="300" selectmode="edit" >
<cfgridcolumn name="id" header="Id" select="false" >
<cfgridcolumn name="name" header="Name" select="false">
<cfgridcolumn name="chkbox" header="checkbox" type="boolean">
</cfgrid>
</cfform>
<cfset ajaxOnLoad("formOnLoad")>