Thursday March 20, 2008 9:25 AM
Word Count: 1740
As you all know by now the grid in ColdFusion 8 is powered by the Ext framework. If you dive into the documentation you will find out that there is more that you can do with it then advertised. We have been looking at ways to customize the grid, today we will learn about event listeners. The grid has the ability to announce events as they are happening even if you are not listening for them. Events can include clicking on a row double clicking on a cell or even using a keyboard shortcut. Just knowing that the gird announces these events will do you no good, you need the ability to listen for them.
The grid gives you 2 methods to listen for events. The following methods will append an event handler to this component the on method is just shorthand for addListener.
- addListener( String eventName, Function handler, [Object scope], [Object options] ) : void
- on( String eventName, Function handler, [Object scope], [Object options] ) : void
These methods belong to the grid so to use them you need to have the grid object. Fortunately for you ColdFusion provides an easy way to get the grid object. If you have been following my examples than the following should look familiar. I am using the cfartgallery data source that ships with CF8 so you can follow along at home. The code below will run a query and create a grid with the name of ArtistGrid. The name is important because that is how we will get our grid object later on. Finally the last line will call the init method when the page loads.
<cfquery name="getArtists" datasource="cfartgallery">
SELECT artistId, firstname, lastname, address, city, state, postalcode, email
FROM Artists
</cfquery>
<cfset args = structNew()>
<cfset args.name = "ArtistGrid">
<cfset args.format = "html">
<cfset args.query = "getArtists">
<cfset args.stripeRows = true>
<cfset args.selectColor = "##D9E8FB">
<cfset args.selectOnLoad = false>
<cfform>
<cfgrid attributeCollection="#args#">
<cfgridcolumn name="artistid" display="false">
<cfgridcolumn name="firstname" header="First Name">
<cfgridcolumn name="lastname" header="Last Name">
<cfgridcolumn name="email" header="Email Address">
<cfgridcolumn name="address" header="Address">
<cfgridcolumn name="city" header="City">
<cfgridcolumn name="state" header="State">
<cfgridcolumn name="postalcode" header="Zip">
</cfgrid>
</cfform>
<cfset ajaxOnLoad("init")>
Now that our grid is setup we can setup our init method. Remember I said ColdFusion makes it easy, I was not lying, the getGridObject will get our grid for us.
<script type="text/javascript">
function init(){
//get the grid component
grid = ColdFusion.Grid.getGridObject("ArtistGrid");
</script>
Now that we have our grid object we can start adding listeners. Lets say we wanted to call a method when a row is clicked. Here we are listening to for a row to be clicked and then calling a method named editArtist. Our editArtist method also get some information about the row being clicked. So How did i know what information would be passed?
<script type="text/javascript">
function init(){
//get the grid component
grid = ColdFusion.Grid.getGridObject("ArtistGrid");
//Fires when a row is clicked
grid.addListener("rowclick",editArtist);
}
function editArtist(grid,rowIndex,e){
var record = grid.getDataSource().getAt(rowIndex);
console.log(record);
}
</script>
If we look at the documentation for the grid event listeners we will get some more details about them. The arguments are what will get passed to use when that event is fired.
- bodyscroll : ( Number scrollLeft, Number scrollTop ) - Fires when the body element is scrolled
- cellclick : ( Grid this, Number rowIndex, Number columnIndex, Ext.EventObject e ) - Fires when a cell is clicked
- cellcontextmenu : ( Grid this, Number rowIndex, Number cellIndex, Ext.EventObject e ) - Fires when a cell is right clicked
- celldblclick : ( Grid this, Number rowIndex, Number columnIndex, Ext.EventObject e ) - Fires when a cell is double clicked
- click : ( Ext.EventObject e ) - The raw click event for the entire grid.
- columnmove : ( Number oldIndex, Number newIndex ) - Fires when the user moves a column
- columnresize : ( Number columnIndex, Number newSize ) - Fires when the user resizes a column
- contextmenu : ( Ext.EventObject e )The raw contextmenu event for the entire grid.
- dblclick : ( Ext.EventObject e ) - The raw dblclick event for the entire grid.
- dragdrop : ( Grid this, Ext.GridDD dd, String targetId, event e ) - Fires when dragged row(s) are dropped on a valid DD target
- dragenter : ( Grid this, Ext.GridDD dd, String targetId, event e ) - Fires when the dragged row(s) first cross another DD target while being dragged
- dragout : ( Grid this, Ext.GridDD dd, String targetId, event e ) - Fires when the dragged row(s) leave another DD target while being dragged
- dragover : ( Grid this, Ext.GridDD dd, String targetId, event e ) - Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dr...
- enddrag : ( Grid this, Ext.GridDD dd, event e ) - Fires when a drag operation is complete
- headerclick : ( Grid this, Number columnIndex, Ext.EventObject e ) - Fires when a header is clicked
- headercontextmenu : ( Grid this, Number columnIndex, Ext.EventObject e ) - Fires when a header is right clicked
- headerdblclick : ( Grid this, Number columnIndex, Ext.EventObject e ) - Fires when a header cell is double clicked
- keydown : ( Ext.EventObject e ) - The raw keydown event for the entire grid.
- keypress : ( Ext.EventObject e ) - The raw keypress event for the entire grid.
- mousedown : ( Ext.EventObject e ) - The raw mousedown event for the entire grid.
- mouseout : ( Ext.EventObject e ) - The raw mouseout event for the entire grid.
- mouseover : ( Ext.EventObject e ) - The raw mouseover event for the entire grid.
- mouseup : ( Ext.EventObject e ) - The raw mouseup event for the entire grid.
- render : ( Grid grid ) - Fires when the grid is rendered
- rowclick : ( Grid this, Number rowIndex, Ext.EventObject e ) - Fires when a row is clicked
- rowcontextmenu : ( Grid this, Number rowIndex, Ext.EventObject e ) - Fires when a row is right clicked
- rowdblclick : ( Grid this, Number rowIndex, Ext.EventObject e ) - Fires when a row is double clicked
- startdrag : ( Grid this, Ext.GridDD dd, event e ) - Fires when row(s) start being dragged
And that is how we are able to find out more information about the event that is happening. In our editArtist method we are getting passed the rowIndex, this allows us to look up what record what clicked. As you can see event listeners can be very powerful and just another way that you can customize your grid components. Please feel free to leave me any questions you might have about them.
Wednesday March 12, 2008 4:35 PM
Word Count: 5712
Earlier today I wrote a short tutorial on grid editing. The tutorial walked you through on how you could easily edit your data right in a grid. While this is great it really is limited to certain use cases and on top of that we are limited with what the user can chose. In our example before we had a bunch of data including the state the artist lives in. If this were a full edit screen we would allow them to select a state from a drop down but in our grid it simply provides them a text box. What if there were a way to customize the editors for each field? Well there is and it really is not that hard. This tutorial will show you a state drop down box and hopefully Ill show you some more in the near future.
Like most of my grid tutorials we are going to use the cfartgallery data source that ships with ColdFusion 8. The first thing we are going to do is create our grid, make it editable and call a function named init after the page loads. If you have been following my grid tutorials this should look famailiar.
<cfquery name="getArtists" datasource="cfartgallery">
SELECT artistId, firstname, lastname, address, city, state, postalcode, email
FROM Artists
</cfquery>
<cfset args = structNew()>
<cfset args.name = "ArtistGrid">
<cfset args.width = 600>
<cfset args.format = "html">
<cfset args.query = "getArtists">
<cfset args.stripeRows = true>
<cfset args.selectColor = "##D9E8FB">
<cfset args.selectmode = "edit">
<cfset args.onchange = "cfc:artists.editArtist({cfgridaction},{cfgridrow},{cfgridchanged})">
<cfform>
<cfgrid attributeCollection="#args#">
<cfgridcolumn name="artistid" display="false">
<cfgridcolumn name="firstname" header="First Name">
<cfgridcolumn name="lastname" header="Last Name">
<cfgridcolumn name="address" header="Address">
<cfgridcolumn name="city" header="City">
<cfgridcolumn name="state" header="State">
<cfgridcolumn name="postalcode" header="Zip">
</cfgrid>
</cfform>
<cfset ajaxOnLoad("init")>
Now comes the real magic. First we need to get our grid object using the built in ColdFusion method getGridObject(). Next we need the column model and finally using the column model we can find out the index of our state column. The reason we need the index number will become clear in a second.
//grid object
grid = ColdFusion.Grid.getGridObject("ArtistGrid");
//column model
cm = grid.getColumnModel();
//we need to know the column id
stIndex = cm.findColumnIndex("STATE");
I am now going to create a combo box which will contain a list of all the states. Right now this will not show up or really do anything.
cb = new Ext.form.ComboBox({
id:"state",
mode:"local",
triggerAction:"all",
displayField:"text",
valueField:"value",
store:new Ext.data.SimpleStore({
fields: ["value", "text"],
data: [
['AL', 'Alabama'],
['AK', 'Alaska'],
['AZ', 'Arizona'],
['AR', 'Arkansas'],
['CA', 'California'],
['CO', 'Colorado'],
['CT', 'Connecticut'],
['DE', 'Delaware'],
['DC', 'District of Columbia'],
['FL', 'Florida'],
['GA', 'Georgia'],
['HI', 'Hawaii'],
['ID', 'Idaho'],
['IL', 'Illinois'],
['IN', 'Indiana'],
['IA', 'Iowa'],
['KS', 'Kansas'],
['KY', 'Kentucky'],
['LA', 'Louisiana'],
['ME', 'Maine'],
['MD', 'Maryland'],
['MA', 'Massachusetts'],
['MI', 'Michigan'],
['MN', 'Minnesota'],
['MS', 'Mississippi'],
['MO', 'Missouri'],
['MT', 'Montana'],
['NE', 'Nebraska'],
['NV', 'Nevada'],
['NH', 'New Hampshire'],
['NJ', 'New Jersey'],
['NM', 'New Mexico'],
['NY', 'New York'],
['NC', 'North Carolina'],
['ND', 'North Dakota'],
['OH', 'Ohio'],
['OK', 'Oklahoma'],
['OR', 'Oregon'],
['PA', 'Pennsylvania'],
['RI', 'Rhode Island'],
['SC', 'South Carolina'],
['SD', 'South Dakota'],
['TN', 'Tennessee'],
['TX', 'Texas'],
['UT', 'Utah'],
['VT', 'Vermont'],
['VA', 'Virginia'],
['WA', 'Washington'],
['WV', 'West Virginia'],
['WI', 'Wisconsin'],
['WY', 'Wyoming']
]
})
});
Finally each column has a defined editor. At the time the grid is built it is usually a text field or number field. To change the editor we need to set a new editor using the column models set editor method. This method takes 2 arguments, the index of the column (we got this earlier) and the new editor. The grid editor takes a form field definition. We are simply adding our combo box but this could easily be a date picker, color picker or custom editor.
cm.setEditor(stIndex,new Ext.grid.GridEditor(cb));
For those of you just joining me I am not running 8 so I can't show you a live demo but here are some images. When you double click the cell the combo box appears with the correct value selected. Then you can choose a state and when the combo box changes a new value is selected a red marker will appear noting that the value has changed.
Here is a the final code used for the example. As always please feel free to leave your questions or comments.
<cfsetting showdebugoutput="false">
<html>
<head>
<title>Edit Artist Grid</title>
<script type="text/javascript">
function init(){
//grid object
grid = ColdFusion.Grid.getGridObject("ArtistGrid");
//column model
cm = grid.getColumnModel();
//we need to know the column id
stIndex = cm.findColumnIndex("STATE");
cb = new Ext.form.ComboBox({
id:"state",
mode:"local",
triggerAction:"all",
displayField:"text",
valueField:"value",
store:new Ext.data.SimpleStore({
fields: ["value", "text"],
data: [
['AL', 'Alabama'],
['AK', 'Alaska'],
['AZ', 'Arizona'],
['AR', 'Arkansas'],
['CA', 'California'],
['CO', 'Colorado'],
['CT', 'Connecticut'],
['DE', 'Delaware'],
['DC', 'District of Columbia'],
['FL', 'Florida'],
['GA', 'Georgia'],
['HI', 'Hawaii'],
['ID', 'Idaho'],
['IL', 'Illinois'],
['IN', 'Indiana'],
['IA', 'Iowa'],
['KS', 'Kansas'],
['KY', 'Kentucky'],
['LA', 'Louisiana'],
['ME', 'Maine'],
['MD', 'Maryland'],
['MA', 'Massachusetts'],
['MI', 'Michigan'],
['MN', 'Minnesota'],
['MS', 'Mississippi'],
['MO', 'Missouri'],
['MT', 'Montana'],
['NE', 'Nebraska'],
['NV', 'Nevada'],
['NH', 'New Hampshire'],
['NJ', 'New Jersey'],
['NM', 'New Mexico'],
['NY', 'New York'],
['NC', 'North Carolina'],
['ND', 'North Dakota'],
['OH', 'Ohio'],
['OK', 'Oklahoma'],
['OR', 'Oregon'],
['PA', 'Pennsylvania'],
['RI', 'Rhode Island'],
['SC', 'South Carolina'],
['SD', 'South Dakota'],
['TN', 'Tennessee'],
['TX', 'Texas'],
['UT', 'Utah'],
['VT', 'Vermont'],
['VA', 'Virginia'],
['WA', 'Washington'],
['WV', 'West Virginia'],
['WI', 'Wisconsin'],
['WY', 'Wyoming']
]
})
});
cm.setEditor(stIndex,new Ext.grid.GridEditor(cb));
}
</script>
</head>
<body>
<link href="/CFIDE/scripts/ajax/ext/resources/css/ytheme-aero.css" rel="stylesheet" type="text/css">
<cfquery name="getArtists" datasource="cfartgallery">
SELECT artistId, firstname, lastname, address, city, state, postalcode, email
FROM Artists
</cfquery>
<cfset args = structNew()>
<cfset args.name = "ArtistGrid">
<cfset args.width = 600>
<cfset args.format = "html">
<cfset args.query = "getArtists">
<cfset args.stripeRows = true>
<cfset args.selectColor = "##D9E8FB">
<cfset args.selectmode = "edit">
<cfset args.onchange = "cfc:artists.editArtist({cfgridaction},{cfgridrow},{cfgridchanged})">
<cfform>
<cfgrid attributeCollection="#args#">
<cfgridcolumn name="artistid" display="false">
<cfgridcolumn name="firstname" header="First Name">
<cfgridcolumn name="lastname" header="Last Name">
<cfgridcolumn name="address" header="Address">
<cfgridcolumn name="city" header="City">
<cfgridcolumn name="state" header="State">
<cfgridcolumn name="postalcode" header="Zip">
</cfgrid>
</cfform>
<cfset ajaxOnLoad("init")>
</body>
</html>
Wednesday March 12, 2008 9:15 AM
Word Count: 1414
When the new grid features were announced in ColdFusion 8 people got very excited. I am not quite sure why but not to many people are talking about this feature these days. If you are new to grid editing support I encourage you to read an article by Ben Forta that outlines how to edit data in a grid. I am going to repeat some of that article but also show you a little more. Think of this as a refresher because the next tutorial I have will dive into some customization techniques.
Getting started with the tutorial we are going to stick with what got us here. I will be using the cfartgallery data source that ships with ColdFusion 8 so this should work out of the box. Here we have the code that creates our grid. There are only 2 attributes we need to add to our grid to make this work. First we set select mode to edit. This tells our grid to add editing capabilities. If we wanted to add deleting capabilities we could set the delete attribute to true but we will just be editing in this tutorial. The second difference is the the onChange attibute. We will look into this a little deeper in a second.
<cfquery name="getArtists" datasource="cfartgallery">
SELECT artistId, firstname, lastname, address, city, state, postalcode, email
FROM Artists
</cfquery>
<cfset args = structNew()>
<cfset args.name = "ArtistGrid">
<cfset args.format = "html">
<cfset args.query = "getArtists">
<cfset args.stripeRows = true>
<cfset args.selectColor = "##D9E8FB">
<cfset args.selectmode = "edit">
<cfset args.onchange = "cfc:artists.editArtist({cfgridaction},{cfgridrow},{cfgridchanged})">
<cfform>
<cfgrid attributeCollection="#args#">
<cfgridcolumn name="artistid" display="false">
<cfgridcolumn name="firstname" header="First Name">
<cfgridcolumn name="lastname" header="Last Name">
<cfgridcolumn name="email" header="Email Address">
<cfgridcolumn name="address" header="Address">
<cfgridcolumn name="city" header="City">
<cfgridcolumn name="state" header="State">
<cfgridcolumn name="postalcode" header="Zip">
</cfgrid>
</cfform>
Here is a how our grid turns out. You will notice that when you double click in a cell a text editor appears and after you make a change a red marker appears in the grid noting that the data has changed.

Now that we understand how to enable editing we need to cover how we actually save the changed data. If you look at the grid image you will notice that the last name Donolan now has a z on the end. If we enable firebug and take a look at the post we will see the following information. As you can see our grid passes 3 parameters; gridAction, gridRow and gridChanged. The gridAction argument will pass U for update and D for delete. The gridRow is a name value pair of the row being changed. Finally the gridChanged argument tells us what column is being changed and what the new value is.
{
"gridaction":
"U",
"gridrow":{
"ARTISTID":1,
"FIRSTNAME":
"Aiden",
"LASTNAME":
"Donolan",
"EMAIL":
"aiden.donolan@donolan.com",
"ADDRESS":
"352 Corporate Ave.",
"CITY":
"Denver",
"STATE":
"CO",
"POSTALCODE":
"80206-4526",
"CFGRIDROWINDEX":1},
"gridchanged":{
"LASTNAME":
"Donolanz"}}
If we look back at our code we should look at the onChange attribute. Here we are telling the grid that when the data changes we want to call our artists cfc and the method editArtist and into that method we are going to pass those 3 parameters.
<cfset args.onchange = "cfc:artists.editArtist({cfgridaction},{cfgridrow},{cfgridchanged})">
Finally here is the code from Ben's tutorial. If we get passed U for update we set the name of the column being changed and the new value and use that to update our database.
<cffunction name="editArtist" access="remote">
<cfargument name="gridaction" type="string" required="yes">
<cfargument name="gridrow" type="struct" required="yes">
<cfargument name="gridchanged" type="struct" required="yes">
<cfset var colname = "">
<cfset var value = "">
<cfswitch expression="#arguments.gridaction#">
<!--- update --->
<cfcase value="U">
<cfset colname = StructKeyList(arguments.gridchanged)>
<cfset value = arguments.gridchanged[colname]>
<cfquery datasource="#variables.dsn#">
UPDATE Artists
SET #colname# = '#value#'
WHERE artistid = <cfqueryparam value="#arguments.gridrow.artistid#" cfsqltype="cf_sql_integer">
</cfquery>
</cfcase>
<!--- delete --->
<cfcase value="D">
<cfquery datasource="#THIS.dsn#">
DELETE FROM Artists
where artistid = <cfqueryparam value="#arguments.gridrow.artistid#" cfsqltype="cf_sql_integer">
</cfquery>
</cfcase>
</cfswitch>
</cffunction>
As you can see editing inline on the grid is very easy to do. I see many question out there about customizing the editing features, stay tuned because I have something you are going to like.
Friday March 7, 2008 9:16 AM
Word Count: 525
The other day I wrote a short article on ColdFusion 8 custom grid toolbars. One reader had a question that I would like to share with you. Trond writes -
Do you know if a similar toolbar can be added to the bottom of a cfwindow?
The answer to this question is two fold. No there is not a panel are on the bottom like there is in the grid or even in the window components in Ext 2.0. This does not prevent you from customizing your windows though. You can add buttons to the window and I will show you how quickly you can do so. After the page loads I am using the ajaxOnLoad method to call my init method.
The first thing I need to do is create my window. You can see I have setup some basic properties on my window and using initShow=true will cause the window to show on page load.
<cfwindow name="MyWindow" center="true" closable="true" draggable="true" height="200" modal="true"
initShow="false" minHeight="100" minWidth="200" resizable="true" title="Notify Administrator" width="400">
Type Message here
</cfwindow>
<cfset ajaxonload("init")>
Here is the only part you need to wrap your head around. When we use the cfwindow tag ColdFusion imports all of the Ext framework libraries necessary to create the window. The button classes are not part of that include so we need to explicitly import them.
<script type="text/javascript" src="/CFIDE/scripts/ajax/ext/package/button/button.js"></script>
Now that we have our button class this is very simple. Here we have our init method that should run after the page is setup. Because there is no attribute of cfwindow for adding buttons we need to create an a event listener on the window. To do that we must first get the window object. After we have the window object we can say here is what we want you to do before you show the window component. The action we want it to do is to add a button. The window has a method for adding a button. The button takes a button config and you can learn more about that in the docs but its pretty straight forward. Finally we add a handler to our button that will call the method sayHello when its clicked.
<script language="JavaScript">
function init(){
myWindow = ColdFusion.Window.getWindowObject('MyWindow');
myWindow.addButton({
text:"My Button",
cls:"x-btn-text-icon",
icon:"add.png",
handler:sayHello
});
}
function sayHello(){
alert("hello");
}
</script>
