ColdFusion 8 cfgrid adding Add and Delete Record Functionality

The ColdFusion 8 cfgrid offers us an excellent interface to allow our application users to perform administrative tasks on table data. Two fundamental functions on table data include the ability to Add a Record and the ability to Delete a Record. So, I modified the cfgrid that has been built till now (blogged previously) to include Add/Delete functionality.

You can look at a Demo Here

For the Add function, we "pop-up" a window asking for information. When the user is done entering that, we call a CFC to add the information to the database and refresh the grid. The Add User button calls the saveUser() JS function that does this.

   view plainprintabout
 function saveUser()
         {
             var objForm = document.addUserForm
             var stcForm = {};
             stcForm.FirstName = objForm.FirstName.value;
             stcForm.LastName = objForm.LastName.value;
             stcForm.DisplayName = objForm.DisplayName.value;
             stcForm.UserName = objForm.UserName.value;
             stcForm.UserAccountingCode = objForm.UserAccountingCode.value;
10              stcForm.Phone = objForm.Phone.value;
11              stcForm.Password = objForm.Password.value;
12              objUser.addUser(stcForm);
13              ColdFusion.Window.hide('addUser');            
14          }
The Delete link simply calls a function called delLink() that pops up a JS confirmation dialog, and if the user clicks Ok, deletes the record.
   view plainprintabout
 function delLink()
         {
             var selectedRecord = grid.getSelections();
             var delID = selectedRecord[0].data.ID;            
             if(confirm('Delete User : ' + selectedRecord[0].data.FIRSTNAME))
             {
                 objUser.delUser(delID);
             };
         }
In order to make the calls to the ColdFusion CFC, we use CFAjaxProxy tag. This was a powerful inclusion in ColdFusion 8 allowing us to call CFCs easily in JS. Read more about it at Livedocs. In the demo, we use it to refer to the Users CFC. Then, in the javascript, we also specify the callback and error handlers.
   view plainprintabout
 <cfajaxproxy cfc="UsersD" jsclassname="Users">
 
 <script>
 var objUser = new Users();
 objUser.setErrorHandler(showError);
 objUser.setCallbackHandler(handleResult);
 </script>
Now, we can use the objUser JS variable to call CFCs specified in the UsersD CFC. In the handleResult callback, we refresh the grid. This will refresh our grid data after the CFC functions to add or delete record has been called.
   view plainprintabout
 function handleResult(res)
 {            
 ColdFusion.Grid.refresh('usersgrid',false);    
 }
The full source code is available Here.
Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Andy Sandefer's Gravatar You can also add an "Edit" button to pull up the form and pre-load it with the selected records fields. In my case, I use the same form as the "Add" and I just pass a javascript variable to the function that creates the cfwindow. When the form loads inside the cfwindow it knows if it is going to Add or Update. The only slight difference in our approaches was in the handling of the database interaction. I went with ColdFusion.Ajax.submit and my form handler either runs an update or insert statement based on whether this was an Add or an Edit. I also had to write other proxy called functions to test whether a primary key already existed in the table before I fired the submit.
# Posted By Andy Sandefer | 1/21/09 6:40 PM
Kumar's Gravatar Yes Andy, that would be an excellent way to handle Edits and hence implement a complete CRUD functionality. I will probably blog about Edit Record feature sometime soon.

I will also look at expanding this to implement form validation and form skinning (CSS).
# Posted By Kumar | 1/21/09 7:19 PM
Glyn Jackson's Gravatar good write up Kumar, yes cfajaxproxy is such powerful tag. I wrote something similar trying to so the same sort of takes with cfgrid

http://www.newebia.co.uk/blog/index.cfm/2009/1/5/C...
# Posted By Glyn Jackson | 1/21/09 7:25 PM
Kumar's Gravatar Glyn,
That looks great. I like how the ExtJS libraries can be extended easily to add toolbar buttons to the grid.

Currently, I have only played with having the options as form buttons outside the cfgrid, I might look into using the toolbar for the Grid Options as the UI does look good in those screenshots.
# Posted By Kumar | 1/21/09 7:32 PM
Glyn Jackson's Gravatar Dan's got some very good example on that and styling linked below

http://www.danvega.org/blog/index.cfm/extjs
# Posted By Glyn Jackson | 1/21/09 7:55 PM
rob's Gravatar I hope someone can help me. I understand how this script works but, I can't get the delete function to work and it's been stumping me for a whole day now... whenever I call the delLInk function it can't seem to find the data in the grid. The alert box says 'Delete user : unknown'. I believe it has something to do with not being able to find the correct identifying row in the grid but for the life of me, I can't see what I did wrong.

My code is:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitiona...;
<html xmlns="http://www.w3.org/1999/xhtml">;
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>

<cfparam name="usersearchterm" default="">

<script type="text/javascript">

var objUser = new usersClass();
      objUser.setErrorHandler(showError);
      objUser.setCallbackHandler(handleResult);



function init()
      {
         grid = ColdFusion.Grid.getGridObject('userSearchResults');
      }
         

function addUser()
{
var objForm = document.addUserForm
var stcForm = {};
         stcForm.UserId = objForm.user_id.value;
         stcForm.UserTypeId = objForm.user_type_id.value;
stcForm.FirstName = objForm.first_name.value;
stcForm.LastName = objForm.last_name.value;
stcForm.Prefix = objForm.prefix.value;
stcForm.AgeRange = objForm.age_range.value;
stcForm.Gender = objForm.gender.value;
stcForm.Address1 = objForm.address1.value;
stcForm.Address2 = objForm.address1.value;
stcForm.City = objForm.city.value;
stcForm.PostalCode = objForm.postal_code.value;
stcForm.Province = objForm.province.value;
         stcForm.Phone1 = objForm.phone_number1.value;
stcForm.Phone2= objForm.phone_number2.value;
stcForm.Phone3 = objForm.phone_number3.value;
stcForm.Country = objForm.country.value;
stcForm.Email = objForm.email.value;
         stcForm.Password = objForm.password.value;
         objUser.addUser(stcForm);
ColdFusion.Window.hide('adduser');
}

function handleResult(res)
      {         
         ColdFusion.Grid.refresh('userSearchResults',false);         
      }
      
function showError()
      {
         alert("An error occured. Please try again.");
      }


function delLink()

      {
         var selectedRecord = grid.getSelections();
         var delID = selectedRecord[0].data.of_user_id;         
         if(confirm('Delete User : ' + selectedRecord[0].data.name))
         {
            objUser.delUser(delID);
         };
      }

</script>
</head>

<body>
<cfajaxproxy cfc="users" jsclassname="usersClass">


<cfajaximport tags="cfform">
<div class="innerWrapper">
<h1>User Administration</h1>
<cfform method="post" action="testing.cfm">
Search users: <cfinput type="text" name="userSearchTerm" value="#userSearchTerm#">&nbsp;<input type="submit" value="Go">&nbsp;&nbsp;

<cfset newuserwindowid = #createuuid()#>
<cfinput type="button" name="addUserButton" id="addUserButton" value="Add User" onclick="makewindow('adduser','#REQUEST.httppath#views/dspAddUser.cfm','Add a user.')">&nbsp;&nbsp;<input type="button" id="importUsersButton" value="Import users from file" />
</cfform>





<h2>Search Results</h2>

<cfform>
<cfgrid name="userSearchResults" format="html" bind="cfc:/ofcomponents.users.getusers({cfgridpage},{cfgridpagesize},{cfgridsortcolumn},{cfgridsortdirection},'#userSearchTerm#')" pagesize="15" width="100%" striperows="yes" striperowcolor="##F5F5F5">

   <cfgridcolumn name="of_user_id" display="no">
<cfgridcolumn name="user_id" header="User ID" width="60">
<cfgridcolumn name="name" header="Name" width="200">
<cfgridcolumn name="active" header="Active" width="50">
<cfgridcolumn name="reg_date" header="Reg. Date" width="90">
<cfgridcolumn name="user_type_description" header="User Type" width="150">
<cfgridcolumn name="email" header="E-mail" width="200">
</cfgrid>

</cfform>
<a href="javascript:delLink()">DELETE USER</a>

<cfset ajaxOnLoad("init")>






</div>

</body>
</html>

If anybody can spot the monkey, please let me know!
# Posted By rob | 6/3/09 6:57 AM
rob's Gravatar OK! I found it... and, I am not sure if this is clear in this code but whenever you reference a column in a grid in your Javascript, the column name MUST BE in upper case! I didn't realize this... but now I do!
# Posted By rob | 6/3/09 7:25 AM
Kumar Shah's Gravatar Sorry for the late reply rob, but yes, when working with Javascript it is important to keep in mind its case sensitive. Especially for ColdFusion developers as we are not used to case sensitivity.
# Posted By Kumar Shah | 6/26/09 12:20 PM
Sandra's Gravatar How can I prevent the cfselect from calling the CFC? Even though we're NOT using onchange in the cfselect, every time the users select a new value in the cfselect the CFC is called and the grid refreshes. Is there any way that this only happens when the user only clicks on the button?

Filer By: <cfselect id="filtercolumn" name="filtercolumn" bind="cfc:UsersD.getUserColumns()"
                     display="ColumnName" value="ColumnName" bindOnLoad="true" />
                  Filter Text: <cfinput type="text" id="filter" name="filter">
                  <cfinput type="button" name="filterbutton" value="Filter" id="filterbutton"
                     onclick="ColdFusion.Grid.refresh('usersgrid',false)">
# Posted By Sandra | 1/29/10 10:05 AM
Kumar Shah's Gravatar Sandra, yes you can do that. If you notice the cfgrid code:
<cfgrid name="usersgrid" pagesize="5" format="html" width="100%" height="200"
bind="cfc:UsersD.getUsers({cfgridpage},{cfgridpagesize},{cfgridsortcolumn},{cfgridsortdirection},getPageSize(),{filtercolumn},{filter})">

The {filtercolumn} binding means that whenever the selection box is changed, the grid is refreshed. To change the behaviour, you would have to remove the filtercolumn binding from the cfgrid declaration.

You can read more about Binding at Adobe's site:
http://livedocs.adobe.com/coldfusion/8/htmldocs/he...
# Posted By Kumar Shah | 1/29/10 11:08 AM
Sandra's Gravatar Hi Kumar,

Thank you so much for your quick reply. I'm new at this and if I can't figure this out soon, then I'm going to have to implement a regular CF search page.. =/

Anyway, If I remove the {filtercolumn} binding nothing happens because the form won't be passing the cfselect value to the CFC when I click on the button. I need to be able to pass the value of the cfselect and cfinput when the user clicks on the button. Otherwise the query won't work.

Thank you so much!
# Posted By Sandra | 1/29/10 11:22 AM
Sandra's Gravatar hmm...perhaps binding the cfselect to a hidden input and then pass the value of the hidden input to the grid... not sure if that will work or if there's something more efficient..I will try that, in the meantime. If there's something more efficient, I'd appreciate if you'd let me know.

Thanks!
# Posted By Sandra | 1/29/10 11:29 AM
Kumar Shah's Gravatar Sandra,

The easiest way to do this would be to bind the cfgrid to a hidden input field. But Do Not bind the hidden input field to the cfselect otherwise when you change the cfselect value, the hidden input field value will also change and the grid will then refresh.

Only change the value of the hidden input field when the user clicks the button, so the button would have an onClick event javascript function that would set the value of the hidden input field to the cfselect value. This way, the cfgrid should only fire when the user clicks the submit button and not when they change the drop-down value. It will also ensure the passing of the cfselect value through the hidden field.
# Posted By Kumar Shah | 1/29/10 11:59 AM
Sandra's Gravatar Thanks Kumar. It worked! Unfortunately when the grid refreshes it triggers the cfwindow that should only display when the user clicks on a row . I don't undestand why if I'm only calling the function on click, not onchange:

<cfajaxproxy bind="javascript:todetail({files.id@click})" />

The CF Ajax Logger displays this when returning the values from the CFC
info:widget: Firing selection change event for grid id: files

:(
# Posted By Sandra | 1/29/10 6:30 PM
Sandra's Gravatar My brain was fried on Friday. I got everything working now. Looks like I needed a break! Thank you so much for your all your help and for sharing your code with everyone else.
# Posted By Sandra | 1/31/10 6:49 PM
KJ's Gravatar I have a bindable cfgrid in cf8 that updates just fine (I see the red triangle in the cell). What I need to know is how to refresh the grid after the update so the original cfc that fills the grid runs again to show new updated information. I found these commands but not sure where to put them or how to call them. Should they be in the page or the cfc? ColdFusion.Grid.refresh('mygrid', true) ColdFusion.Grid.getGridObject('MyGrid').render();
# Posted By KJ | 6/1/10 9:00 PM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.5.003.  Design based on ARCLITE by: digitalnature