Binding data to form fields

Many ColdFusion Ajax features use binding to provide dynamic interactivity based on user input or changing data. When you use binding, a bind expression gets evaluated, and the display gets updated based on new data each time a specific event (onChange by default) occurs on a form control field specified by a bind parameter. This way, the value of the tag that specifies the bind expression, and the display, get updated dynamically based on changing information, including user-entered form data. When you use binding the page contents updates, but the entire page is not refreshed.

Note: When a bound window is not visible, or a tab is not selected, its contents is not updated when the controls it is bound to change. When the tab or window is made visible, it is updated only if events have been received from the bound controls while the control was not visible.

Depending on the specific ColdFusion tag, a bind expression uses bind parameter values directly or passes bind parameter values as parameters to a CFC function, a JavaScript function, or an HTTP request and uses the function or request response to update the page. Use the following as the data source for a bind expression:

  • ColdFusion form control attributes and values. You can bind to the following controls:

    • cfgrid

    • cfinput with checkbox, datefield, file, hidden, radio, or text types

    • cfselect

    • cftextarea

    • cftree

  • Spry data set elements

Note: You cannot use a bind expression to bind to controls in a dynamically loaded region. For example, you cannot bind from a control on one page to a control in a layout area on that page if the cflayoutarea tag uses a source attribute for its contents. However, a dynamically loaded region binds to controls on the page that loads it, so the file specified by the source attribute uses bind expressions that specify controls on the page that contains the cflayoutarea tag.

The results of the bind expression determine the value of the tag that uses the expression. For example, if you specify a URL in a bind expression as the source attribute of a cfwindow control, the page specified by the URL must return the full contents of the window.

For more examples, see Using Ajax User Interface Components and Features and the reference pages for controls that support binding.

Using bind expressions

To specify a bind expression, use one of the following formats:

  • cfc:componentPath.functionName(parameters)

    Note: In ColdFusion 9, the component path cannot use a mapping. The componentPath value must be a dot-delimited path from the web root or the directory that contains the current page.
  • javascript:functionName(parameters)

  • url:URL?parameters

  • URL?parameters

  • A string containing one or more instances of {bind parameter}, such as {firstname}.{lastname}@{domain}

In formats 1-4 the parameters normally include one or more bind parameters. The following table lists the tag attributes that support bind expressions and the formats each use:

Attribute

Tags

Supported formats

autosuggest

cfinput type="text"

1, 2, 3

bind

cfdiv, cfinput, cftextarea

1, 2, 3, 5

bind

cfajaxproxy, cfgrid, cfselect, cfsprydataset, cftreeitem

1, 2, 3

onChange

cfgrid

1, 2, 3

source

cflayoutarea, cfpod, cfwindow

4

The following examples show some of these uses:

bind="cfc:myapp.bookorder.getChoices({book})" 
source="/myApp/innerSource/cityWindow.cfm?cityname={inputForm:city}

In these examples, {book} and {inputForm:city} specify bind parameters that dynamically get data from the book and city controls, and the city control is in the inputForm form.

If a bind attribute specifies a page that defines JavaScript functions, the function definitions on that page must have the following format:

functionName = function(arguments) {function body}

Function definitions that use the following format may not work:

function functionName (arguments) {function body}

However, Adobe recommends that you include all custom JavaScript in external JavaScript files and import them on the application’s main page, and not write them inline in code that you get using the source attribute. Imported pages do not have this function definition format restriction.

Specifying bind parameters

A bind parameter specifies a form control value or other attribute, as in the following example:

bind="cfc:myapplication.bookSearch.getStores({form1:bookTitle})"

In this example, the bind parameter is form1:bookTitle and specifies the value attribute of the bookTitle field of the form1 form.

Bind parameters have either of the following formats:

{[formName:]controlName[.attributeName][@event]} 
{SpryDataSetName.fieldName}

The brackets ([]) indicate optional contents and are not part of the parameter.

Note: To include a literal brace character in a bind expression, escape the character with a backslash, as \{, \}.

The formname value

The formname entry identifies the form that contains the control you are binding to. Specify a form name if multiple forms contain bind targets with the same names. To specify the form name, start the bind expression with the form’s id attribute the name attribute if you did not specify an id attribute, and follow it with a colon (:). To specify the book control that is in a form named inputForm, for example, use the following format:

bind="cfc:myapp.bookorder.getChoices({inputForm:book})"

The controlName value

To bind to a form field, the controlName value must be the value of the id or name attribute of the form control to which you are binding. If a control has both an id and a name attribute, use either value.

You can bind to any ColdFusion form control, including cfgrid and cftree. You cannot bind to values in other ColdFusion tags, such as cftable.

To bind to a Spry data set, specify the data set name in this part of the bind parameter.

You can bind to multiple radio buttons or check boxes by giving them the same name value. If all the radio buttons in a radio button group have the same name value, the bind parameter represents the selected button. If multiple check boxes have the same name value, the bind parameter represents the values of the selected controls in a comma-delimited list. If you also specify a unique id attribute for each check box or radio button, specify an HTML label tag for each button or check box and use the id value in the for attribute; in this case, users select items by clicking the label, not just the button or box.

If a cfselect control supports multiple selections, the bind expression returns the information about the selected items in a comma-delimited list.

You can bind only to controls that are available in the DOM tree when the bind is registered. Binds are registered when the page with the bind expression loads, either in the browser window or in a container tag. As a result, if you have two cfdiv, cflayoutarea, cfpod, or cfwindow containers that you load by using a source (or for cfdiv tag, bind) attribute, you cannot bind controls in one container to controls in the other, because one container cannot be assured that the other is loaded when it loads. Similarly, elements on the main page cannot bind to elements on a dynamically loaded container. To prevent this problem, define the bind target in line on the main page, instead of using a source or bind attribute to retrieve the markup that contains the bind target. In other words, the “master” form with fields that serve as sources of bind expressions is loaded statically (on the main page), and the “child” controls that depend on the data are loaded dynamically, on a page that is specified in a source or bind attribute.

The attributeName value

When you bind to a form control, by default, the bind expression represents the value attribute of the specified control. If the bind target is a cfselect tag, the bind expression represents a comma delimited list of the values of the selected items.

To bind to a different attribute, follow the control name or id with a period (.) and the attribute name. To pass the checked attribute of a checkbox cfinput tag as a CFC parameter, for example, use an expression such as the following:

bind="cfc:myapp.bookorder.useStatus({myForm:approved.checked@click})
Note: You can bind to the display text of a select box, instead of the value, by specifying an attribute name of innerHTML.
Note: When you bind to a check box, use the @click event specifier to ensure that the bind expression is triggered in Internet Explorer when the user selects or deselects the check box, not when the box loses focus.

Grids and trees do not have default bind attributes.

  • Always specify a grid target attribute by using the format {gridID.columnName}. The bind expression gets the value of the specified column in the selected row.

  • For trees, you must bind to a specific node in the tree. Specify the node by using the node ID or an explicit path to the node.

To bind to a Spry data set element or attribute, use standard Spry path notation. For example, specify an element name.

The event value

By default, the bind expression function executes each time the control specified in the bind parameter has an onChange event. To trigger updates on a different JavaScript event, end the bind expression with an at sign (@) and the event name, without the “on” prefix. The following code, for example, executes the getChoices CFC each time the user presses the mouse button while the pointer is over the book control:

bind="cfc:myapp.bookorder.getChoices({inputForm:book@mousedown})"
Note: To bind to a cfinput control with type attribute of button, specify a bind event setting, such as click. The change event is the default event has no effect.

When you bind to a Spry data set, do not specify an event. The expression is evaluated when the selected row changes in the data set, or when the data set reloads with new data.

You can also specify that a specific bind parameter never triggers bind expression reevaluation, by specifying @none as the event. This is useful, for example, if a bind expression uses multiple bind parameters binding to different form fields, and you want the bind expression to trigger changes only when one of the fields changes, not when the others change. In this case, you would specify @none for the remaining fields, so events from those fields would not trigger the bind. The following code shows this use:

bind="cfc:books.getinfo({iForm:book}, {iForm:author@none})"

The @none event specifier is also useful when used with autosuggest text inputs, trees and grids, as follows:

  • When you use an autosuggest text input, the bind expression is evaluated as a user types in text, and picks up data from all bind parameters, including those parameters with @none specified. Therefore, for autosuggest, specify @none for all bind parameters, because there is no way for it to react to changes in the parameters.

  • When you call the ColdFusion.Grid.refresh or ColdFusion.Tree.refresh function, the function fetches data from all bind parameters when it evaluates the bind expression, including any parameters with @none specified. If you specify @none for all bind parameters, the tree or grid might not respond to changes in other controls, but gets data from all the bind parameters each time you explicitly refresh it.

Using CFC functions in bind expressions

As with JavaScript functions, you pass arguments to a CFC function specified in a bind expression positionally. When you do this, the argument names in a CFC function definition do not have to be the same as the bind parameter names, but the arguments in the bind expression must be in the same order as the arguments in the CFC function definition.

Alternatively, you pass named CFC function arguments. Then, the bind expression and CFC function must use the same names for the arguments, and the function does not have to define the arguments in the same order as they are passed. To specify argument names in a bind expression, use a format such as the following, which uses two named parameters, arg1 and arg2:

bind="cfc:mycfc.myfunction(arg1={myform:myfield1},arg2={myform:myfield2})"

Support for CFCs outside webroot

Note: To use this feature, you must install ColdFusion 9 Update 1.

Components outside the webroot can be accessed in bind expressions. This implies that tags such as cfajaxproxy or Ajax components such as grid, map, or progress bar can be used in more effective ways.

Note: In the previous releases, the CFCs had to be web-accessible for Ajax applications to function.

In addition to accessing CFCs using relative or absolute path, you can also use any of the following methods to access CFCs:

  • logical mappings (defined in the ColdFusion Administrator)

  • per-app mappings (defined in Application.cfc)

  • imports (using cfimport/import)

Usage

The following code shows the usage of this enhancement using per-map mappings:

Application.cfc

THIS.mappings["/mycfc"] = "C:\www\shared\components";

Test.cfm

<cfajaxproxy cfc="mycfc.utils" jsclassname='jsobjname' />

Example

In this example, a per-app mapping named mycfcs has been created in Application.cfc pointing to "c:\components". For the sample code to work, create a folder named components in your system root (in this example, c:\) and copy the Employee.cfc to that folder.

Application.cfc

<cfcomponent> 
    <cfset this.name = "cfcoutsidewebroot"> 
    <cfset this.sessionmanagement = true> 
    <Cfset mappingname = "/mycfcs"> 
    <Cfset mappingpath = "c:\components\"> 
    <cfset this.mappings[mappingname] = mappingpath> 
</cfcomponent>    

Employee.cfc

<cfcomponent> 
    <cfscript> 
        remote any function getEmployees(page,pageSize,gridsortcolumn="EMP_ID",gridsortdirection="ASC"){ 
                var startRow = (page-1)*pageSize; 
                var endRow = page*pageSize; 
                 
                if(!isdefined("arguments.gridsortcolumn") or isdefined("arguments.gridsortcolumn") and trim(arguments.gridsortcolumn) eq "") 
                    gridsortcolumn = "EMP_ID"; 
                if(!isdefined("arguments.gridsortdirection") or isdefined("arguments.gridsortdirection") and arguments.gridsortdirection eq "") 
                    gridsortdirection = "ASC"; 
                var mysql = "SELECT Emp_ID, FirstName,  EMail, Department FROM Employees"; 
                if(isdefined("arguments.gridsortcolumn") and arguments.gridsortcolumn neq "") 
                    mysql = mysql & " ORDER BY " & gridsortcolumn; 
                if(isdefined("arguments.gridsortdirection") and arguments.gridsortdirection neq "") 
                    mysql = mysql & " " & gridsortdirection ; 
                rs1 = new query(name="team", datasource="cfdocexamples", sql=mysql).execute(); 
                   return QueryConvertForGrid(rs1.getResult(), page, pageSize); 
        } 
     
     
        remote any function editEmployees(gridaction,gridrow,gridchanged){ 
                writelog("edit employee info"); 
        } 
     
    </cfscript> 
</cfcomponent>

Employee.cfm

<cfform> 
    <cfgrid 
            format="html" 
            name="grid01" 
            pagesize=10 
            title="Employee database" 
            bind="cfc:mycfcs.employee.getEmployees({cfgridpage},{cfgridpagesize},{cfgridsortcolumn},{cfgridsortdirection})" 
            onChange="cfc:mycfcs. employee.editEmployees({cfgridaction},{cfgridrow},{cfgridchanged})"> 
            <cfgridcolumn name="Emp_ID" display=false header="ID" /> 
            <cfgridcolumn name="FirstName" display=true header="First Name"/> 
            <cfgridcolumn name="Email" display=true header="Email"/> 
            <cfgridcolumn name="Department" display=true header="Department" /> 
    </cfgrid> 
</cfform>

Using binding in control attributes

When you use direct binding you specify a bind expression in a ColdFusion form or display control attribute. In the simplest, form of binding you use form fields, such as a name field, to fill other fields, such as an e-mail field, as the following example. shows. When you enter a name or domain and tab to click in another field, the name is added to the e-mail field.

<html> 
<head> 
</head> 
<body> 
 
<cfform name="mycfform"> 
    First Name: <cfinput type="text" name="firstname" value=""><br> 
    Last Name: <cfinput type="text" name="lastname" value=""><br> 
    Domain: <cfinput type="text" name="domain" value=""><br> 
    E-mail: <cfinput type="text" name="email1" size="30" 
        bind="{firstname}.{lastname}@{domain}">  
</cfform> 
</body> 
</html>

The following example shows the results of binding to radio buttons and check boxes with the same name attribute but different id attributes. Notice that because each control has a separate id value that is used in the label tags, you click the labels to select and deselect the controls.

<html> 
<head> 
</head> 
<body> 
 
<cfform name="myform"> 
    Pick one: 
    <cfinput id="pickers1" name="pickone" type="radio" value="Apples"> 
        <label for="pickers1">Apples</label> 
    <cfinput id="pickers2" name="pickone" type="radio" value="Oranges"> 
        <label for="pickers2">Oranges</label> 
    <cfinput id="pickers3" name="pickone" type="radio" value="Mangoes"> 
        <label for="pickers3">Mangoes</label> 
    <br> 
    <cfinput name="pickone-selected" bind="{pickone}"><br /> 
    <br /> 
 
    Pick as many as you like: 
    <cfinput id="pickers4" name="pickmany" type="checkbox" value="Apples"> 
        <label for="pickers4">Apples</label> 
    <cfinput id="pickers5" name="pickmany" type="checkbox" value="Oranges"> 
        <label for="pickers5">Oranges</label> 
    <cfinput id="pickers6" name="pickmany" type="checkbox" value="Mangoes"> 
        <label for="pickers6">Mangoes</label> 
    <br/> 
    <cfinput name="pickmany-selected" bind="{pickmany}"><br /> 
 
</cfform> 
</body> 
</html>

Most applications call a CFC function, or JavaScript function, or use a URL to make an HTTP request (typically to a CFML page), and pass bind parameters as the function or URL parameters.

The following example uses the same form as the first example in the preceding section, but uses a different bind expression with the following features:

  • It uses the keyup events of the name and domain fields to trigger binding. So the e-mail field gets updated each time that you enter a letter in any of these fields.

  • It calls a CFC, which uses only the first letter of the first name when forming the e-mail address, and forces the domain name to be all lowercase.

The following example shows the bindapp.cfm page:

<html> 
<head> 
</head> 
<body> 
<cfform name="mycfform"> 
    First Name: <cfinput type="text" name="firstname" value=""><br> 
    Last Name: <cfinput type="text" name="lastname" value=""><br> 
    Domain: <cfinput type="text" name="domain" value=""><br> 
    E-mail: <cfinput type="text" name="email" 
        bind="cfc:bindFcns.getEmailId({firstname@keyup},{lastname@keyup}, 
        {domain@keyup})">  
</cfform> 
</body> 
</html>

The following example shows the bindFcns.cfc CFC file:

<cfcomponent> 
    <cffunction name="getEmailId" access="remote"> 
        <cfargument name="firstname">  
        <cfargument name="lastname">  
        <cfargument name="domain">  
        <cfreturn "#left(arguments.firstname,1)#.#arguments.lastname#@#lcase(arguments.domain)#"> 
    </cffunction> 
</cfcomponent>

Many of the examples in the documentation for ColdFusion Ajax features use binding, including more complex forms of binding.

Using the cfajaxproxy tag to bind to display controls

The cfajaxproxy tag with a bind attribute makes any of the following elements dependent on one or more bound ColdFusion Ajax controls:

  • A single CFC function

  • A single JavaScript function

  • An HTTP request; for example, the URL of a CFML page

The function or request executes whenever a specific event (by default, the onChange event) of the bound control occurs.

Note: if you specify a bind attribute with a URL, the HTTP request includes a _CF_NODEBUG URL parameter. ColdFusion checks this value, and when it is true, does not append to the response any debugging information that it normally would send. This behavior ensures that JSON responses to Ajax requests do not include any non-JSON (that is, debugging information) text.

The cfajaxproxy tag includes the following attributes that determine how the proxy handles the data returned by the function or the page:

  • The onError function specifies code to handle an HTTP error return. You use this attribute with a URL or CFC bind.

  • The onSuccess function handles a valid return from the function or page and updates the display as required with the returned information.

Binding a function or request by using the cfajaxproxy tag enables you to perform a server-side action, such as updating a database by using bind parameter values based on a user action in some control, and then run a specific action or set of actions in one or more controls based on the server response. Because it uses an onSuccess function to process the return from the server, this form of binding provides substantially more flexibility than a CFML control bind parameter. This format also lets you use a control bind parameter for one kind of action, and the cfajaxproxy tag for a different activity.

For example, if you have a form with an editable cfgrid control and a delete button that a user clicks to delete a grid row. The application must have the following behaviors:

  • When the user clicks the delete button two things must happen:

    • The application must call a mycfc.deleteButton CFC function to delete the row from the database.

    • The grid must update to remove the deleted row.

  • When the user edits the grid content, the grid must call a mycfc.update function to update the database.

Implement these behaviors by doing the following:

  • In the cfgrid tag, specify a bind attribute that uses a bind expression to call a mycfc.update function each time the user changes the grid contents.

  • In a cfajaxproxy tag, specify a bind attribute that calls the mycfc.deleterow CFC function, and specify an onSuccess attribute that calls the ColdFusion.Grid.refresh function to update the displayed grid when the CFC function returns successfully.

The following code snippets show how you could do this:

<cfajaxproxybind="cfc:mycfc.deleteRow({deletebutton@click}, 
        {mygrid.id@none}"onSuccess="ColdFusion.Grid.refresh('mygrid', true)"> 
 
... 
 
<cfinput type="button" name="deletebutton"> 
<cfgrid name="mygrid" bind="mycfc.update({cfgridpage}, {cfgridpagesize}, 
        {cfgridsortcolumn}, {cfgridsortdirection})>

The following complete example shows a simple use of the bind attribute in a cfajaxproxy tag. For the sake of brevity, the bind expression calls a JavaScript function; as a result, the cfajaxproxy tag cannot use a onError attribute.

<html> 
<head> 
<script language="javascript"> 
    function test(x,y){ 
        return "Hello, " + x + "!"; 
    } 
    function callbackHandler(result){ 
        alert("Bind expression evaluated. Result: \n" + result); 
    } 
</script> 
 
<cfajaxproxy bind="javascript:test({input1@none},{button1@click})"  
        onSuccess="callbackHandler"> 
</head> 
 
<body> 
<cfform name="mycfform"> 
        <cfinput type="text" value="" name="input1" size="30"> 
        <cfinput type="button" name="button1" value="Submit"> 
</cfform> 
</body> 
</html>

Getting bindable attribute values in JavaScript

Use the ColdFusion.Ajax.submitForm function in your JavaScript code to get the current value of any attribute of a bindable control. This technique is useful for getting values for complex controls such as cfgrid and cftree. For more information, see the ColdFusion.Ajax.submitForm function in the CFML Reference.