I gave a presentation today at the local ColdFusion User Group of Central New York (CFUGCNY) on the efficient use of [brackets] in <CFML> and thought I would share the slides and examples, so here they are.

The presentation covered the following topics:

  • What is this bracket thing all about?
  • Object.property notation
  • Associative array notation
  • How it all works
  • Embedding brackets
  • Mixing it up
  • Avoiding Evaluate()
  • Looping over arrays, structures, and queries
  • Using brackets with objects

Example 1

In this example, lets say you work for a bank.  You have been supplied with two datasets, one is a structure that contains accounts and their beginning balances, and looks like this:

The second dataset is a transaction log, which is a query that contains the account number, the amount of the transaction, and a field that specifies the type of transaction; whether it is a debit or a credit, and look like this:

Your boss has asked you to create a report for each account with the ending balance.  So the first step is to create a structure that has the ending balance for each account that you will use in your report.  So lets look at the code:

?View Code COLDFUSION
1
2
3
4
5
6
7
8
9
<cfset balanceStr = StructNew()>
<cfset StructAppend(balanceStr,budgetStr)>
<cfloop query="transactionsQry">
    <cfif UCase(transactionsQry.type) eq 'CR'>
        <cfset balanceStr[transactionsQry.account] = (balanceStr[transactionsQry.account] + transactionsQry.amount)>
    <cfelseif UCase(transactionsQry.type) eq 'DB'>
        <cfset balanceStr[transactionsQry.account] = (balanceStr[transactionsQry.account] - transactionsQry.amount)>
    </cfif>
</cfloop>

Let me try to explain the code real quickly:

  1. First, we are creating a new structure to hold the ending balances for each account called balanceStr.
  2. Then, I am creating a duplicate of the budgetStr (the first dataset about that contains beginning balances)
  3. Next, we loop through the transactionsQry query, and depending on the type of transaction we add or subtract the amount from the appropriate account’s balance in the balanceStr structure.

Field names example

In this example, I am showing how you can easily work with dynamic field names through the use of the form.fieldnames list provided by CF.  First, we have a simple form that allows people to provide their personal information that looks like this:

?View Code COLDFUSION
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<form action="<cfoutput>#cgi.script_name#</cfoutput>?action=submit" method="post">
    <p><strong>Bold</strong> fields are required.</p>
    <fieldset>
        <legend>Personal Information</legend>
        <div class="notes">
            <h4>Personal Information</h4>
            <p class="last">Please enter your name and address as you would like it to appear on your invoice.</p>
        </div>
        <div class="required">
            <label for="first_name">First Name:</label>
            <input name="first_name" id="first_name" class="inputText" size="10" maxlength="100" value="" type="text">
        </div>
        <div class="required">
            <label for="last_name">Last Name:</label>
            <input name="last_name" id="last_name" class="inputText" size="10" maxlength="100" value="" type="text">
        </div>
        <div class="optional">
            <label for="address_1">Address:</label>
            <input name="address_1" id="address_1" class="inputText" size="10" maxlength="100" value="" type="text">
            <input name="address_2" id="address_2" class="inputText" size="10" maxlength="100" value="" type="text">
        </div>
        <div class="optional">
            <label for="city">City:</label>
            <input name="city" id="city" class="inputText" size="10" maxlength="100" value="" type="text">
        </div>
    </fieldset>
    <fieldset>
        <div class="submit">
            <div>
                <input type="image" src="images/submit.png" class="submitbutton" />
            </div>
        </div>
    </fieldset>
</form>

Then, on submit I am outputting the form variables names along with their values by looping through the fieldnames list and using bracket notation to obtain the values:

?View Code COLDFUSION
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<p>The values you entered were:</p>   
<table cellpadding="2" cellspacing="2" style="border:1px solid #c0c0c0;">
    <tr style="background:#e9e9e9;">
        <th>Key</th>
        <th>Value</th>
    </tr>
    <cfloop list="#form.fieldnames#" index="i">
        <cfoutput>
            <tr>
                <td>#i#</td>
                <!--- Avoiding Evaluate --->
                <td>#form[i]#</td>
 
                <!--- Using Evaluate --->
                <!--- <td>#Evaluate("#i#")#</td> --->
            </tr>
        </cfoutput>
    </cfloop>
</table>

You can also see that I have showed both methods; using the evaluate() function and using associative array notation.

Guest form example

This is another quick example that shows how to use dynamic variable names with the form scope.  In this example, I am outputting an arbitrary number of checkboxes using the naming convention "guest1", "guest2"…  I also provide a quick and easy way to check or uncheck all of the checkboxes using a function I wrote VERY quickly (thanks prototype!).

?View Code COLDFUSION
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<script language="javascript" type="text/javascript">
    function toggleCheckAll(formelem,bChecked){
        $(formelem).getInputs('checkbox').each(function(elem){
            elem.checked = bChecked;
        })
    }
</script>
 
<form action="<cfoutput>#cgi.script_name#</cfoutput>?action=submit" method="post" id="guestform">
    <p><strong>Bold</strong> fields are required.</p>
    <fieldset>
        <legend>Personal Information</legend>
        <div class="notes">
            <h4>Personal Information</h4>
            <p class="last">Please enter the names of your guests.</p>
        </div>
        <div class="optional">
            <fieldset>
                <legend>Check / Uncheck all guests</legend>
                <a href="javascript:void(0);" onclick="toggleCheckAll('guestform',true)">Check all</a><br />
                <a href="javascript:void(0);" onclick="toggleCheckAll('guestform',false)">Uncheck all</a>
            </fieldset>
        </div>
        <cfloop from="1" to="#iterationcount#" index="i">
            <cfoutput>
                <div class="optional">
                    <label for="guest#i#">Guest #i#:</label>
                    <input name="guest#i#" type="checkbox" value="1" />
                </div>
            </cfoutput>
        </cfloop>
    </fieldset>
    <fieldset>
        <div class="submit">
            <div>
                <input type="image" src="images/submit.png" class="submitbutton" />
            </div>
        </div>
    </fieldset>
</form>

Upon submission, I am then outputting the guest number and the value (1 if it is checked) for the arbitrary number of guest inputs.  I am using both the evaluate() method and the associative array (or bracket notation as I like to call it) notation with CFTIMER to compare the two methods:

?View Code COLDFUSION
1
2
3
4
5
6
7
8
9
10
11
12
<cftimer label="using evaluate()" type="outline">
    <cfloop from="1" to="#iterationcount#" index="i">
        <cfoutput>guest#i#: #evaluate("form.guest#i#")#</cfoutput>
    </cfloop>
</cftimer>
 
<br /><br />
<cftimer label="avoiding evaluate()" type="outline">
    <cfloop from="1" to="#iterationcount#" index="i">
        <cfoutput>guest#i#: #form['guest'&i]#</cfoutput>
    </cfloop>
</cftimer>

Here is a graph of the results from the CFTIMER test using 100, 200, 1000, and 5000 checkboxes.

Interestingly enough, when I ran this example live during my presentation, the times were much different on my Mac Book Pro than on my Windows Vista machine (both using CF8 developer edition).  There was very little differences of the timing when running on the Mac, and they times were MUCH lower (aka, it ran A LOT faster).  Perhaps this is due to the OS architecture, or just the amount of available memory.

Looping example

Here are some easy ways to loop over complex structures in ColdFusion using bracket notation.  We can easily loop over the elements of a ColdFusion array using brackets and the for loop:

?View Code COLDFUSION
1
2
3
4
5
6
7
8
<cfscript>
 myArray = ['Array value 1', 'Array value 2', 'Array value 3'];
 
 //loop over array and output to browser
 for(i=1; i <= ArrayLen(myArray); i++){
  WriteOutput(myArray[i] & '<br />');
 }
</cfscript>

We can loop over the elements in a ColdFusion structure using brackets and the for-in loop:

?View Code COLDFUSION
1
2
3
4
5
6
7
8
9
10
11
<cfscript>
 myStr = StructNew();
 myStr["key1"] = "Struct value 1";
 myStr.key2 = "Struct value 2";
 myStr.key3 = "Struct value 3";
 
 //loop over struct and output to browser
 for(key in myStr){
  WriteOutput(myStr[key] & '<br />');
 }
</cfscript>

We can easily loop over the elements of a ColdFusion query object using brackets and the for loop:

?View Code COLDFUSION
1
2
3
4
5
6
7
8
9
10
11
12
<cfscript>
 myQry = QueryNew("values");
 for (i=1; i lte 3; i++){
     QueryAddRow(myQry);
     QuerySetCell(myQry,"values","Query value "&i);
 }
 
 //loop over array and output to browser
 for(i=1; i <= myQry.recordcount; i++){
  WriteOutput(myQry['values'][i] & '<br />');
 }
</cfscript>

Objects example

In this example, I show how you can use bracket notation to obtain function references dynamically in order to call custom get/set functions.  Lets say we have a CFC with generic getter and setter methods, i.e.

  • userObj.get(’name’);
  • userObj.set(’name’,form.name);

Where we have a column in our table called name, and we are either getting the value that is stored in the DB, or we are setting a new value from a form element with a name attribute equal to ‘name’.

If a custom function is defined, we would like to call that function when we call the generic get/set method.  The custom functions in this example are named getname() for the name column, or get___ for whatever column you are using.  When this method exists within the CFC, we will call that method instead of returning the data as normal.  In this example, I am using a custom getphone() method that formats the phone number before returning the value to the calling template.

The generic get function might look something like this:

?View Code COLDFUSION
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<cffunction name="get" output="false" access="public">
   <cfargument name="field" required="yes" />
   <cfset var newfuncname = "" />
 
   <cfif structkeyexists(variables,'get#arguments.field#')
     AND isCustomFunction(variables['get'&arguments.field])>
 
     <cfset newfuncname = variables['get'&arguments.field] />
     <cfreturn newfuncname() />
 
   </cfif>
 
   …return value from data struct…
 </cffunction></typo:code lang="xml"></blockquote><br />
<br />
So, if we defined a custom get function like this:<br />
<br />
<blockquote><pre lang="ColdFusion"><cffunction name="getphone" access="private" output="false">
    <cfif IsDefined('variables.datafields.#arguments.field#.value')>
        <cfreturn NumberFormat(variables.datafields[arguments.field].value,"999-9999" />
    <cfelse>
        <cfreturn "undefined" />
    </cfif>
</cffunction>

This custom function would be called since it exists, and it will return the phone number that is formatted properly when we call userObj.get(’phone’).

More Info?

This entry was posted on Tuesday, October 9th, 2007 at 4:30 pm.
Categories: Uncategorized.

One Comment, Comment or Ping

  1. Don Blaire

    Thanks. I was looking for the ability to check all checkboxes or clear all checkboxes.

Reply to “Bracketology CFUG presentation”