ColdFusion HTML format forms and controls provide the following Ajax-based features:
ColdFusion Ajax-based form controls let you submit Ajax forms in your applications without refreshing the entire page.
Using Ajax containers for form submission
The ColdFusion Ajax container tags, cfdiv, cflayoutarea, cfpod, and cfwindow, automatically submit any forms that they contain asynchronously. When the form is submitted, the result returned by the action page replaces the contents of the container, but has no effect on the rest of the page.
The following example shows this behavior in the submitSimple.cfm page:
<html xmlns="http://www.w3.org/1999/xhtml"> <head> </head> <body> <cflayout type="vbox" name="layout1"> <cflayoutarea> <h3>This area is not refreshed when the form is submitted.</h3> <br /> </cflayoutarea> <cflayoutarea> <h3>This form is replaced by the action page</h3> <cfform name="myform" format="html" action="showName.cfm"> <cfinput type = "Text" name = "name"> <cfinput type = "submit" name = "submit" value = "Enter name"> </cfform> </cflayoutarea> </cflayout> </body> </html>
In the following example, when you enter a name in the text input and click the Enter name button, the entered text replaces the form on the page, but the rest of the page is not refreshed. This example shows the showName.cfm action page:
<cfif IsDefined("Form.name")> <cfoutput>The Name is : <strong>#Form.name#</strong></cfoutput> </cfif>
Using the cfajaxproxy SetForm function
The SetForm function of the proxy object created by the cfajaxproxy tag causes the proxy to pass the form values as arguments to the next CFC function that you call after the SetForm function. This way, you can pass the current values of fields in a form to a CFC function, which can then do the necessary processing and return a result.
When you use the SetForm function, the following rules apply to the arguments in the called CFC function:
The following example shows how you could use a the SetForm tag to submit the contents of a login form. When the user clicks the Login! button, the doLogin function calls the proxy setForm function and then the AuthenticationSystem.cfc validateCredentials method. The validateCredentials method checks the user's password and if it is valid, returns true to the proxy. Because the proxy is synchronous (the default), the doLogin method gets the returned value. If the value is true, it hides the login window; the user can then access the page contents. If the return value is false, the doLogin function displays a message in the login window title bar.
The following examples shows the setForm.cfm application:
<html> <head> <script type="text/javascript"> function doLogin() { // Create the ajax proxy instance. var auth = new AuthenticationSystem(); // setForm() implicitly passes the form fields to the CFC function. auth.setForm("loginForm"); //Call the CFC validateCredentials function. if (auth.validateCredentials()) { ColdFusion.Window.hide("loginWindow"); } else { var msg = document.getElementById("loginWindow_title"); msg.innerHTML = "Incorrect username/password. Please try again!"; } } </script> </head> <body> <cfajaxproxy cfc="AuthenticationSystem" /> <cfif structKeyExists(URL,"logout") and URL.logout> <cflogout /> </cfif> <cflogin> <cfwindow name="loginWindow" center="true" closable="false" draggable="false" modal="true" title="Please login to use this system" initshow="true" width="400" height="200"> <!--- Notice that the form does not have a submit button. Submission is done by the doLogin function. ---> <cfform name="loginForm" format="xml"> <cfinput type="text" name="username" label="username" /><br /> <cfinput type="password" name="password" label="password" /> <cfinput type="button" name="login" value="Login!" onclick="doLogin();" /> </cfform> </cfwindow> </cflogin> <p> This page is secured by a login. You can see the window containing the login form. The window is modal so the page cannot be accessed until you login. <ul> <li><a href="setForm.cfm">Continue using the application</a>!</li> <li><a href="setForm.cfm?logout=true">Logout</a>!</li> </ul> </p> </body> </html>
The following examples shows the AuthenticationSystem.cfc file:
<cfcomponent output="false"> <cffunction name="validateCredentials" access="remote" returntype="boolean" output="false"> <cfargument name="username" type="string"/> <cfargument name="password" type="string"/> <cfset var validated = false/> <!--- Ensure that attempts to authenticate start with new credentials. ---> <cflogout/> <cflogin> <cfif arguments.username is "user" and arguments.password is "secret"> <cfloginuser name="#arguments.username#" password="#arguments.password#" roles="admin"/> <cfset validated = true/> </cfif> </cflogin> <cfreturn validated/> </cffunction> </cfcomponent>
Using the ColdFusion.Ajax.submitForm function
You can use the ColdFusion.Ajax.submitForm function to submit form contents to a CFML page (or other active page) at any time. For example, you could use this function to automatically save a partially completed form.
When you use this function, you pass it the name of the form to submit and the URL of the page that processes the form. You can also specify the following optional parameters:
The following proof of concept example uses the ColdFusion.Ajax.submitForm function to submit two form fields to an asyncFormHandler.cfm page, which simply echoes the form values. The callback handler displays an alert with the returned information.
<html> <head> <!--- The cfajaximport tag is required for the submitForm function to work because the page does not have any Ajax-based tags. ---> <cfajaximport> <script> function submitForm() { ColdFusion.Ajax.submitForm('myform', 'asyncFormHandler.cfm', callback, errorHandler); } function callback(text) { alert("Callback: " + text); } function errorHandler(code, msg) { alert("Error!!! " + code + ": " + msg); } </script> </head> <body> <cfform name="myform"> <cfinput name="mytext1"><br /> <cfinput name="mytext2"> </cfform> <a href="javascript:submitForm()">Submit form</a> </body> </html>
The asynchFormHandler.cfm page consists of a single line, as follows:
<cfoutput>Echo: #form.mytext1# #form.mytext2#</cfoutput>
Using the ColdFusion.navigate function to submit a form
The ColdFusion.navigate JavaScript function can submit a form to a URL and have the returned output appear in a specified container control, such as a cfdiv, cflayout, cfpod, or cfwindow tag. This function lets you populate a control other than the one that contains the form when the user submits the data. You can also use the function to submit the form asynchronously when a user performs an action outside the form, such as clicking on a menu item.
For an example that uses this function, see the ColdFusion.navigate function in the CFML Reference.
The ColdFusion HTML format cfgrid control lets you use a bind expression to dynamically populate the grid. HTML format grids that use bind expressions are paged; as users navigate from page to page of the grid, the grid dynamically gets the data for only the required page from the data source. You also use bind expressions when you let users edit form contents, and other ColdFusion controls can bind to the grid. Also, HTML format grids provide several JavaScript functions that you can use to manage and manipulate the grids.
You can also create a static HTML format grid by specifying a cfgrid tag that does not use a bind expression. With static grids, all data is initially available.
HTML format grids can dynamically fill the grid data by using a bind attribute with a bind expression that calls a CFC or JavaScript function, or a URL. The bind expression uses bind parameters to specify dynamic information provided by the grid and the values of any other form field attributes.
You must pass the following bind parameters to the bind expression. If you omit any of the parameters in the function call or URL, you get an error. These parameters send information about the grid and its state to the data provider function. The data for these parameters is provided automatically. You do not set any values manually.
Parameter name |
Description |
---|---|
cfgridpage |
The number of the page for which to retrieve data. |
cfgridpagesize |
The number of rows of data in the page. The value of this parameter is the value of the pageSize attribute. |
cfgridsortcolumn |
The name of the column that determines the sorting order of the grid. This value is set only after the user clicks on a column heading. |
cfgridsortdirection |
The direction of the sort, may be 'ASC' (ascending) or 'DESC' (descending). This value is set only after the user clicks on a column heading. |
For more information on binding and bind parameters, see "Using Ajax Data and Development Features" in the CFML Reference.
You can use optional parameters to specify additional information to pass to the called function. These parameters provide data that the called function requires to determine the data to return. For example, if the function returns the cities in a state, you would pass it the state name. Any or all of the optional function parameters can be bind parameters. A state name, for example, could come from the selection in a states cfselect control.
If you do not want the grid to refresh automatically when other controls change, you can use the @none specifier on all optional bind parameters. This prevents automatic updating of the grid based on the bound control values. You use the ColdFusion.Grid.refresh JavaScript function to explicitly refresh the grid contents. For more information on this use of the @none specifier and explicitly refreshing the control, see "Specifying bind parameters".
If the grid supports user sorting of the data (the sort attribute is true), the function called by the bind expression must return data in the desired sorted order, and must use the values of the cfgridsortcolumn and cfgridsortdirection bind parameters to determine the order. Even if you do not allow user sorting, you must still pass these parameters to the function; otherwise, you get an error. Also, your function or action page must handle cases where these parameters are empty strings, because their values are not set until the user selects a column header to sort the grid, or you call the JavaScript ColdFusion.Grid.sort function.
The format of the returned data depends on how you get the data:
Bind type |
Return value |
---|---|
CFC |
A ColdFusion structure. ColdFusion automatically converts the structure for return to the caller. Alternatively, you can return a JSON representation of the structure. |
URL |
A JSON representation of a structure. No other body contents is allowed. |
JavaScript |
A JavaScript object. |
When you specify a CFC in the bind attribute, use the queryConvertForGrid function to convert a query directly into a structure that you can use as your CFC return value.
When you specify a CFML page in the bind attribute, use the queryConvertForGrid function to convert a query into a structure, and then use the serializeJSON function to convert the structure into a JSON representation.
If you manually create a JavaScript object or its JSON representation, it must have two top-level keys:
The following example defines an object that a JavaScript bind function can return to provide the data for a cfgrid tag:
var myobject = {"TOTALROWCOUNT":6,"QUERY":{"COLUMNS":["EMP_ID","FIRSTNAME", "EMAIL"],"DATA":[[1,"Carolynn","CPETERSON"], [2,"Dave","FHEARTSDALE"], [3,"Linda","LSTEWART"], [4,"Aaron","ASMITH"], [5,"Peter","PBARKEN"], [6,"Linda","LJENNINGS"],]}};
The following example uses a bind expression and a CFC to populate a dynamic, paged, data grid. The CFML page contains the following form:
<html xmlns="http://www.w3.org/1999/xhtml"> <head> </head> <body> <cfform name="form01"> <cfgrid format="html" name="grid01" pagesize=5 sort=true bind="cfc:places.getData({cfgridpage},{cfgridpagesize}, {cfgridsortcolumn},{cfgridsortdirection})"> <cfgridcolumn name="Emp_ID" display=true header="eid" /> <cfgridcolumn name="FirstName" display=true header="Name"/> <cfgridcolumn name="Email" display=true header="Email" /> </cfgrid> </cfform> </body> </html>
The places.cfc file looks as follows. Notice that the query gets the full data set each time the function gets called. the QueryConvertForGrid function selects and returns only the required page of data:
<cfcomponent> <cffunction name="getData" access="remote" output="false"> <cfargument name="page"> <cfargument name="pageSize"> <cfargument name="gridsortcolumn"> <cfargument name="gridsortdirection"> <cfquery name="team" datasource="cfdocexamples"> SELECT Emp_ID, FirstName, EMail FROM Employees <cfif gridsortcolumn neq "" or gridsortdirection neq ""> order by #gridsortcolumn# #gridsortdirection# </cfif> </cfquery> <cfreturn QueryConvertForGrid(team, page, pageSize)> </cffunction> </cfcomponent>
The following example is equivalent to the previous one, but uses a URL bind expression in the main page and a CFML page to return the data.
The main page contains the following form:
<html xmlns="http://www.w3.org/1999/xhtml"> <head> </head> <body> <cfform name="form01"> <cfgrid format="html" name="grid01" pagesize=5 sort=true bind="url:getdata.cfm?page={cfgridpage}&pageSize={cfgridpagesize} &sortCol={cfgridsortcolumn}&sortDir={cfgridsortdirection}"> <cfgridcolumn name="Emp_ID" display=true header="eid" /> <cfgridcolumn name="FirstName" display=true header="Name"/> <cfgridcolumn name="Email" display=true header="Email" /> </cfgrid> </cfform> </body> </html>
The following examples shows the getdata.cfm page:
<!--- Empty string, the default end of the query SQL. ---> <cfset queryEnd=""> <cfquery name="team" datasource="cfdocexamples"> SELECT Emp_ID, FirstName, EMail FROM Employees <cfif sortcol neq "" or sortdir neq ""> order by #sortcol# #sortdir# </cfif> </cfquery> <!--- Format the query so the bind expression can use it. ---> <cfoutput>#serializeJSON(QueryConvertForGrid(team, page, pageSize))# </cfoutput>
If your database lets you specify SQL to retrieve only the required page of data in a query, you can optimize efficiency by using such a query. Do not use the QueryConvertForGrid function. Instead, manually create the return structure and return only the single page of data. Ensure that you set the TotalRowCount field to the number of rows in the entire data set, not the number of rows in the returned page of data.
Using the bindOnLoad attribute
The bindOnLoad attribute causes a control to execute its bind expression immediately when it loads, and not wait until the event that normally triggers the bind expression evaluation to occur. This way, the control can be filled with an initial value. This attribute is false by default for all ColdFusion Ajax controls that have the attribute, except cfdiv and cfgrid, for which it is true by default. Having a true bindOnLoad value on these controls ensures that they are populated when they load.
When a control with a true bindOnLoad attribute is bound to a control that also binds when the page loads, the first and second control load themselves at the onLoad page event. Then the first control loads itself again in response to a change event from the second control when that control completes loading. So, the first control makes two Ajax calls, whereas it should make only one, when the second control finished loading.
Because the cfinput, cfselect, and cftextarea control bindOnLoad attributes are false by default, you do not encounter any problems if a cfgrid or cfdiv tag binds to any of these controls and you do not explicitly set the their bindOnLoad attributes. However, if the control does set its bindOnLoad attribute to true, you should set the cfgrid or cfdiv attribute to false to ensure that the control only fetches data when the control that it is bound to returns.
You can also get a double loading if a grid binds to a Spry data set. By default, the grid and data set load data at page load, and then the grid loads data again in response to a selection change event from the data set when the it sets focus to its first row. Set bindOnLoad to false to ensure that the grid fetches data only when it receives a selection change event from the data set.
Dynamically editing grid contents
When you use a bind expression to get cfgrid data dynamically, you can also update the data source dynamically with user input, without requiring the user to submit the form. You can use dynamic updating to update or delete data in the data source. (To edit cfgrid data, select the contents of a field and type the new value; to delete a row, select a field in the row and click the delete button at the bottom of the grid.)
You cannot insert new rows directly in a grid that uses a bind expression. To add rows, you must enter the data in a form, and make sure the grid refreshes after the form has been submitted.
To update or delete data dynamically, do the following:
Parameter name |
Description |
---|---|
cfgridaction |
The action performed on the grid. 'U' for update, or 'D' for delete. |
cfgridrow |
A structure or JavaScript Object whose keys are the column names and values are the original values of the updated or deleted row. |
cfgridchanged |
A structure or JavaScript Object with a single entry, whose key is the name of the column with the changed value, and whose value is the new value of the field. If the grid action is delete, this structure exists but is empty |
When you update data dynamically, you can also use the onError attribute to specify the name of a JavaScript function to handle any errors that result in a CFC or URL returning an HTTP error status. The method must take two parameters: the HTTP error code and a text message that describes the error. The following example shows an onError handler function:
<script type="text/javascript"> function errorhandler(id,message) { alert("Error while updating \n Error code: "+id+" \nMessage: "+message);} </script>
The following example displays the members of a department and lets users edit the data in the fields. When the focus leaves the edited field an onChange event triggers and the form calls the editData CFC function to update the data source.
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <script type="text/javascript"> function errorhandler(id,message) { alert("Error while updating\n Error code: "+id+"\n Message: "+message); } </script> </head> <body> <cfform name="form01"> <cfgrid format="html" name="grid01" pagesize=11 stripeRows=true stripeRowColor="gray" bind="cfc:places.getData({cfgridpage},{cfgridpagesize}, {cfgridsortcolumn},{cfgridsortdirection})" delete="yes" selectmode="edit" onchange="cfc:places.editData({cfgridaction},{cfgridrow},{cfgridchanged})"> <cfgridcolumn name="Emp_ID" display=true header="Employee ID"/> <cfgridcolumn name="FirstName" display=true header="Name"/> <cfgridcolumn name="Email" display=true header="Email"/> </cfgrid> </cfform> </body> </html>
The getData function is identical to the getData function in "Dynamically filling form data". This example shows the editData function in the CFC:
<cffunction name="editData" access="remote" output="false"> <cfargument name="gridaction"> <cfargument name="gridrow"> <cfargument name="gridchanged"> <cfif isStruct(gridrow) and isStruct(gridchanged)> <cfif gridaction eq "U"> <cfset colname=structkeylist(gridchanged)> <cfset value=structfind(gridchanged,#colname#)> <cfquery name="team" datasource="cfdocexamples"> update employees set <cfoutput>#colname#</cfoutput> = '<cfoutput>#value#</cfoutput>' where Emp_ID = <cfoutput>#gridrow.Emp_ID#</cfoutput> </cfquery> <cfelse> <cfquery name="team" datasource="cfdocexamples"> delete from employees where emp_id = <cfoutput>#gridrow.Emp_ID# </cfoutput> </cfquery> </cfif> </cfif> </cffunction>
Binding controls to grid contents
You can bind the contents of a form control to the data in a grid field by specifying a bind parameter as the form control bind attribute value. To do so, use the following syntax:
<cfinput type="text" bind="{gridName.columnName}">
By default, each time the selected row in the grid changes, the bind parameter is reevaluated, and the control value changes to the value of the specified column of selected grid cell.
You can use the following JavaScript functions to manage an HTML format grid:
Function |
Description |
---|---|
ColdFusion.Grid.getGridObject |
Gets the underlying Ext JS JavaScript library object. |
ColdFusion.Grid.refresh |
Manually refreshes a displayed grid. |
ColdFusion.Grid.sort |
Sorts the grid. |
For more information, see the ColdFusion.Grid.getGridObject, ColdFusion.Grid.refresh, and ColdFusion.Grid.sort functions in the CFML Reference.
An HTML format cftree tag creates an Ajax-based tree data representation that you can populate from a query or a bind expression. The behavior with a query is equivalent to that of applet or Flash format trees. Bind expressions let you populate the tree based on the values of other controls or Spry data sets. Also, when you use a bind expression, the tree loads dynamically, getting only the data required for the current display.
Populating the tree using a bind expression
You use the bind attribute and bind expressions to dynamically and incrementally load and display tree data as the user navigates the tree. The child tree items do not exist until the parent node expands. This behavior avoids prefilling a tree with large amounts of data, lets the tree children change dynamically (you can optionally get the children each time the item expands), and can enhance application responsiveness.
For more information about binding and bind parameters, see "Binding data to form fields".
Bind expressions in trees work in the following ways:
When you use a bind expression to populate a cftree control, you must specify a CFC function, JavaScript function, or URL, and must pass it the following bind parameters. If you omit either of the parameters from your function call or URL, you get an error. These parameters provide information about the tree and its state, and are automatically provided by the control.
Bind parameter |
Description |
---|---|
{cftreeitempath} |
Passes the path in the of the current (parent) node to the method, which will use it to generate the next node. |
{cftreeitemvalue} |
Passes the current tree item value (normally the value attribute) |
The called function or URL cannot return nested arrays and structures, that is, it can only return a single level of items.
When a function or URL is first called to populate the root-level tree items, the value passed in the cftreeitemvalue variable is the empty string. Your bind function can test for an empty string to determine that it is populating the root level of the tree.
The @none event specifier is also useful if you use the ColdFusion.Tree.refresh JavaScript function to manually refresh the tree. When you call the Refresh function, the bind expression fetches data from all bind parameters, including @none parameters. If you specify @none in all bind parameters that specify other controls, the tree does not respond automatically to changes in the other controls, but it does pick up data from the bind parameters when you use the ColdFusion.Tree.Referesh function to explicitly refresh the tree.
The format of the data that the function or URL in a bind expression must return depends on the type of bind expression
Bind type |
Return value |
---|---|
CFC |
A ColdFusion array of structures. ColdFusion automatically converts the structure to JSON format when it returns the result to the caller. Alternatively, you can return a JSON representation of the structure. |
JavaScript |
A JavaScript Array of Objects. |
URL |
A JSON representation of an array of structures. No other body content is allowed. |
Each structure in the array of structures or objects defines the contents and appearance of the node for a child item. Each structure must have a VALUE field, and can have the following fields. With the exception of LEAFNODE, these structure keys correspond to cftreeitem attributes.
The LEAFNODE structure element is only used in the bind response structures. It must be a Boolean value that identifies whether the node is a leaf. If the value is true, the tree does not show a +/- expansion indicator in front of the node, and users cannot expand the node.
If your bind expression specifies a JavaScript function, the function must use all-uppercase letters for the field names; for example, use VALUE and DISPLAY, not value and display. ColdFusion uses all capital letters in the structure key names. ColdFusion is case-insensitive, so CFCs can use lowercase letters for the field names; JavaScript is case-sensitive, so the JavaScript function must match the uppercase field names.
If you use a URL to get the tree items from a CFML page, you can use the serializeJSON function to convert the array to JSON format. If the array with the tree items is named itemsArray, for example, the following line specifies the page output:
<cfoutput>#serializeJSON(itemsArray)#</cfoutput>