Adobe ColdFusion 8

Managing the client-server interaction

You can manage the client-server interaction in several ways:

  • Use the cfajaxproxy tag to create a client-side JavaScript proxy for a CFC and its functions. You can then call the proxy functions in client JavaScript code to access the server-side CFC functions.
  • Use the cfsprydataset tag to dynamically populate a Spry data set from a URL or a CFC. You can then use the data set to populate Spry dynamic regions. You can also use Spry data sets in bind expressions.
  • Use the cfajaxproxy tag to bind fields of ColdFusion Ajax form controls as parameters to a specific CFC function, JavaScript function, or HTTP request, and specify JavaScript functions to handle successful or error results. The function is invoked each time the event determined by the bind expression occurs.
  • Use ColdFusion Ajax-based UI tags, such as cftree or cfgrid that automatically get data from CFCs or URLs by using data binding.

For Information on working with Spry, including how to use the cfsprydataset tag, see Using Spry with ColdFusion. For detailed information on using binding, including how to use binding with ColdFusion UI tags and the cfajaxproxy tag, see Binding data to form fields. For more information on using the ColdFusion Ajax-based UI tags, see Using Ajax UI Components and Features.

Using ColdFusion Ajax CFC proxies

You can use the cfajaxproxy tag to create a client-side JavaScript proxy for a CFC and its functions. The proxy object has the following characteristics:

  • It provides a JavaScript function the corresponds to each CFC remote function. Calling these functions in your client-side JavaScript code remotely calls the CFC functions on the server.
  • It provides JavaScript support functions for controlling the communication, which specifies asynchronous result and error handler callbacks, and sends form data to the server. For detailed information on these functions, see "CFC proxy utility functions" in the cfajaxproxy tag in the CFML Reference.
  • It manages the interactions between the client and the CFC, including serializing and deserializing JavaScript arrays and structures to and from JSON format for transmission over the web.
  • It ensures automatic serialization (into JSON format) and deserialization of CFC return values.

By using a ColdFusion Ajax proxy, any JavaScript code can call the proxied CFC functions. Thus, any Ajax application, not just one the uses ColdFusion Ajax UI elements, can use dynamic data provided by CFCs. Also, the proxy provides access to all of the functions in a CFC, not just the single function that you can specify in a bind expression.

Creating a JavaScript CFC proxy

The cfajaxproxy tag with a cfc attribute generates a JavaScript proxy that represents a CFC on the web client. Because a ColdFusion page that uses the cfajaxproxy tag is used as an Ajax client web page, the page typically starts with the cfajaxproxy tag (or tags), and the remainder of the page consists of the HTML and JavaScript required to control the display and perform the page logic on the client.

Note: Because JavaScript is case-sensitive, you must make sure that you match the case of the keys in any ColdFusion structure or scope that you send to the client. By default, ColdFusion sets variable names and structure element names to all-uppercase. (You can create structure element names with lowercase characters by specifying the names in associative array notation, for example, myStruct["myElement"]="value".) The keys for the two arrays in the JSON object that the ColdFusion SerializeJSON function generates to represent a query are COLUMNS and DATA, for example, not columns and data.

For more information about creating and using CFC proxies, see the cfajaxproxy tag in the CFML Reference.

Configuring the CFC proxy

The proxy provides several JavaScript functions that you use to control the behavior of the proxy:

  • You use the setAsyncMode and setSyncMode functions to control the call mode. By default, all calls to remote CFC functions are asynchronous, the most common synchronization method for Ajax applications.
  • You use the setCallbackHandler and setErrorHandler functions to specify the functions that handle the results of successful and unsuccessful asynchronous calls.

Note: For error handling to work properly, you must select the Enable HTTP Status Codes option on the Server Settings > Settings page of the ColdFusion Administrator.

  • You use the setHTTPMethod function to control whether the call uses a GET HTTP request (the default) or a POST request.
  • You use the setForm function to prepare the proxy to send full form data to the remote function. This function causes the proxy to pass each form field as a separate parameter to the CFC function.
  • You use the setReturnFormat function to specify whether to return the result in JSON format (the default), in WDDX format, or as plain text. You use the setQueryFormat function to specify whether to return a JSON format query as an object with an array of column names and an array of row arrays, or as an object that corresponds to the WDDX query format. These functions only effect the format of data returned by ColdFusion. Data sent from the proxy to the server is always in JSON format.

Submitting data to a CFC

When you use an Ajax CFC proxy, you can send to the CFC function any client-side data that can be serialized to JSON format, not just form data. However, the proxy cannot serialize DOM tree elements because they are wrappers on native code. Therefore, you cannot use DOM tree elements directly as parameters to a CFC function that you call by using an Ajax proxy. To ensure correct serialization to JSON for sending to the CFC, you must use basic JavaScript types only: array, object, and simple types. Instead of using a DOM element directly, you can pass only the specific element attributes that you require to the CFC function, either individually or in an array or object.

When you use the cfc attribute, you can submit form data to the CFC without refreshing the client page by calling the proxy setForm function before you call a CFC proxy function in your JavaScript. The proxy function then passes all field values of the specified form to the CFC function. In the CFC function Arguments scope, the argument names are the form control ID attributes (or, by default, the name attributes) and the argument values are the control values.

Note: You cannot use the setForm function to submit the contents of file fields.

To pass the form parameters to your proxy function, you must invoke the proxy function immediately after you call the setForm function. Subsequent proxy function invocations do not get the form parameters.

If you also pass arguments explicitly to the CFC, cfargument tags in the CFC function that specify the explicitly passed arguments must precede any cfargument tags for the form fields. For example, you might have the following submitForm JavaScript function:

function submitForm() {
    var proxy = new remoteHandler();
    proxy.setCallbackHandler(callbackHandler);
    proxy.setErrorHandler(errorHandler);
    proxy.setForm('myform');
    proxy.setData('loggedIn');
}

In this example, the remoteHandler.cfc setData function should start as follows:

    <cffunction name="setData" access="remote" output="false">
        <cfargument name="loggedIn">
        <cfargument name="userName">
    ...

In this example, userName is the name of a form field. If the cfargument tag for userName preceded the cfargument tag for the loggedIn explicitly passed variable, the CFC function would not get the value of loggedIn. Your CFC function can omit cfargument tags for the form fields.

Example: Using an asynchronous CFC proxy

The following example uses a remote CFC method to populate a drop-down list of employees. When you select a name from the list, it uses a call to the CFC method to get information about the employee, and displays the results.

The main application page has the following lines:

<!--- The cfajaxproxy tag creates a client-side proxy for the emp CFC.
        View the generated page source to see the resulting JavaScript. 
        The emp CFC must be in the components subdirectory of the directory
        that contains this page. --->
<cfajaxproxy cfc="components.emp" jsclassname="emp">

<html>
    <head>
        <script type="text/javascript">
        
            // Function to find the index in an array of the first entry 
            // with a specific value.
            // It is used to get the index of a column in the column list.
            Array.prototype.findIdx = function(value){
                for (var i=0; i < this.length; i++) {
                    if (this[i] == value) {
                        return i;
                    }
                }
            }

            // Use an asynchronous call to get the employees for the 
            // drop-down employee list from the ColdFusion server.
            var getEmployees = function(){
                // Create an instance of the proxy. 
                var e = new emp();
                // If you set a callback handler for the proxy, the proxy's calls
                // are asynchronous.
                e.setCallbackHandler(populateEmployees);
                e.setErrorHandler(myErrorHandler);
            // The proxy getEmployees function represents the CFC 
            // getEmployees function.
                e.getEmployees();
            }
            
            // Callback function to handle the results returned by the 
            // getEmployees function and populate the drop-down list.
            var populateEmployees = function(res)
            {
                with(document.simpleAJAX){
                    var option = new Option();
                    option.text='Select Employee';
                    option.value='0';
                    employee.options[0] = option;
                    for(i=0;i<res.DATA.length;i++){
                        var option = new Option();
                        option.text=res.DATA[i][res.COLUMNS.findIdx('FIRSTNAME')]
                            + ' ' + res.DATA[i][[res.COLUMNS.findIdx('LASTNAME')]];
                        option.value=res.DATA[i][res.COLUMNS.findIdx('EMP_ID')];
                        employee.options[i+1] = option;
                    }
                }    
            }

            // Use an asynchronous call to get the employee details. 
            // The function is called when the user selects an employee.
            var getEmployeeDetails = function(id){
                var e = new emp();
                e.setCallbackHandler(populateEmployeeDetails);
                e.setErrorHandler(myErrorHandler);
            // This time, pass the employee name to the getEmployees CFC
            // function.
                e.getEmployees(id);
            }
            // Callback function to display the results of the getEmployeeDetails
            // function.
            var populateEmployeeDetails = function(employee)
            {
                var eId = employee.DATA[0][0];
                var efname = employee.DATA[0][1];
                var elname = employee.DATA[0][2];
                var eemail = employee.DATA[0][3];
                var ephone = employee.DATA[0][4];
                var edepartment = employee.DATA[0][5];

                with(document.simpleAJAX){
                    empData.innerHTML = 
                    '<span style="width:100px">Employee Id:</span>' 
                    + '<font color="green"><span align="left">' 
                    + eId + '</font></span><br>' 
                    + '<span style="width:100px">First Name:</span>' 
                    + '<font color="green"><span align="left">' 
                    + efname + '</font></span><br>' 
                    + '<span style="width:100px">Last Name:</span>' 
                    + '<font color="green"><span align="left">'     
                    + elname + '</font></span><br>' 
                    + '<span style="width:100px">Email:</span>'
                    + '<font color="green"><span align="left">' 
                    + eemail + '</span></font><br>' 
                    + '<span style="width:100px">Phone:</span>' 
                    + '<font color="green"><span align="left">' 
                    + ephone + '</font></span><br>' 
                    + '<span style="width:100px">Department:</span>' 
                    + '<font color="green"><span align="left">' 
                    + edepartment + '</font></span>';
                }
            }

            // Error handler for the asynchronous functions.
            var myErrorHandler = function(statusCode, statusMsg)
            {
                alert('Status: ' + statusCode + ', ' + statusMsg);
            }
            
        </script>
    </head>

    <body>
        <!--- The form to display the employee drop-down list and 
        employee data. --->
        <form name="simpleAJAX" method="get">
            List of Employees:&nbsp;&nbsp;&nbsp;
            <select name="employee" onChange="getEmployeeDetails(this.value)">
                <script language="javascript">
                    getEmployees();
                </script>
            </select>
            <br><br>
            <span id="empData"></span>
        </form>    
    </body>
</html>

The following component, which gets the data from the data source, must be in a file named emp.cfc in the components subdirectory of the application directory. The CFC uses the cfdocexamples data source that is installed with ColdFusion if you install the documentation.

<cfcomponent>
    <cfset this.dsn = "cfdocexamples">
    <cffunction name="getEmployees" access="remote" returnFormat="json" output="false">
        <cfargument name="empid" required="no" type="string" default="0">
        <Cfquery name="qryEmp" datasource="#this.dsn#">
            select * from Employees 
            <cfif empid neq 0>
            where Emp_ID = #empid#
            </cfif>
        </Cfquery>
        <cfreturn qryEmp>
    </cffunction>
</cfcomponent>