This section discusses the advanced topics of dynamic expressions, dynamic evaluation, and dynamic variable naming. Many ColdFusion programmers never encounter or need to use dynamic expressions. However, dynamic variable naming is important in situations where the variable names are not known in advance, such as in shopping cart applications.
This section also discusses the use of the IIF
function which is most often used without dynamic expressions. This function dynamically evaluates its arguments, and you must often use the DE
function to prevent the evaluation. For more information on using the IIF
function, see "Using the IIF function".
Note: This section uses several tools and techniques that are documented in later chapters. If you are unfamiliar with using ColdFusion forms, structures, and arrays, you should learn about these tools before reading this section.
Dynamic variables are variables that are named dynamically, typically by creating a variable name from a static part and a variable part. For example, the following example dynamically constructs the variable name from a variable prefix and a static suffix:
<cfset "#flavor#_availability" = "out of stock">
Using dynamic variables in this manner does not require dynamic evaluation.
In a dynamic expression, the actual expression, not just its variable values, is determined at execution time. In other words, in a dynamic expression the structure of the expression, such as the names of the variables, not just the values of the variables, gets built at runtime.
You create dynamic expressions using string expressions, which are expressions contained in strings, (that is, surrounded with quotation marks). Dynamic evaluation is the process of evaluating a string expression. The Evaluate
and IIF
functions, and only these functions, perform dynamic evaluation.
When ColdFusion performs dynamic evaluation it does the following:
This process enables ColdFusion to interpret dynamic expressions with variable parts. However, it incurs a substantial processing overhead.
Dynamic expressions were important in early versions of ColdFusion, before it supported arrays and structures, and they still can be useful in limited circumstances. However, the ability to use structures and the ability to use associative array notation to access structure elements provide more efficient and easier methods for dynamically managing data. For information on using arrays and structures, see Chapter 5, "Using Arrays and Structures".
The following two examples describes cases when you need dynamic variable names:
In this case, the custom tag does not know the return variable name in advance, and gets it as an attribute value.
In both cases, it might appear that dynamic expressions using the Evaluate
function are needed to construct the variable names. However, you can achieve the same ends more efficiently by using dynamic variable naming, as shown in "Example: a dynamic shopping cart".
This does not mean that you must always avoid dynamic evaluation. However, given the substantial performance costs of dynamic evaluation, you should first ensure that one of the following techniques cannot serve your purpose:
While ColdFusion does not always allow you to construct a variable name in-line from variable pieces, it does let you to do so in the most common uses, as described in the following sections.
You can combine text and variable names to construct a variable name on the left side of a cfset
assignment. For example, the following code sets the value of the variable Product12 to the string "Widget":
<cfset ProdNo = 12>
<cfset "Product#ProdNo#" = "Widget">
To construct a variable name this way, all the text on the left side of the equal sign must be in quotation marks.
This usage is less efficient than using arrays. The following example has the same purpose as the previous one, but requires less processing:
<cfset MyArray=ArrayNew(1)>
<cfset prodNo = 12> <cfset myArray[prodNo] = "Widget">
When you use a dynamic variable name in quotes on the left side of an assignment, the name must be either a simple variable name or a complex name that uses object.property notation (such as MyStruct.#KeyName#). You cannot use an array as part of a dynamic variable name. For example, the following code generates an error:
<cfset MyArray=ArrayNew(1)>
<cfset productClassNo = 1> <cfset productItemNo = 9> <cfset "myArray[#productClassNo##productItemNo#]" = "Widget">
However, you can construct an array index value dynamically from variables without using quotes on the left side of an assignment. For example, the preceding sample code works if you replace the final line with the following line:
<cfset myArray[#productClassNo# & #productItemNo#] = "Widget">
The ability to use associative array notation to reference structures provides a way for you to use variables to dynamically create structure references. (For a description of associative array notation, see "Structure notation," in Chapter 5.) Associative array structure notation allows you to use a ColdFusion expression inside the index brackets. For example, if you have a productName structure with keys of the form product_1, product_2 and so on, you can use the following code to display the value of productName.product_3:
<cfset prodNo = 3>
<cfoutput> Product_3 Name: #ProductName["product_" & prodNo]# <cfoutput>
For an example of using this format to manage a shopping cart, see "Example: a dynamic shopping cart".
The following sections describe how to use dynamic evaluation and create dynamic expressions.
The following table describes the functions that perform dynamic evaluation and are useful in evaluating dynamic expressions:
Function |
Purpose |
---|---|
DE |
Escapes any double quotes in the argument and wraps the result in double quotes. The DE function is particularly useful with the IIF function, to prevent the function from evaluating a string to be output. |
Evaluate |
Takes one or more string expressions and dynamically evaluates their contents as expressions from left to right. (The results of an evaluation to the left can have meaning in an expression to the right.) Returns the result of evaluating the rightmost argument. For more information on this function see "About the Evaluate function". |
IIF |
Evaluates a boolean condition expression. Depending on whether this expression is True or False, dynamically evaluates one of two string expressions and returns the result of the evaluation. The IIF function is convenient for incorporating a cfif tag in-line in HTML. For an example of using this function, see "Using the IIF function". |
SetVariable |
Sets a variable identified by the first argument to the value specified by the second argument. This function is no longer required in well-formed ColdFusion pages; see "SetVariable function considerations". |
It is important to remember that ColdFusion always evaluates function arguments before the argument values are passed to a function:
For example, consider the following DE
function:
<cfoutput>#DE("1" & "2")#</cfoutput>
You might expect this line to display """1"" & ""2""". Instead, it displays "12", because ColdFusion processes the line as follows:
DE
function.
DE
function, which adds literal quotation marks around the 12. Similarly, if you use the expression DE(1 + 2), ColdFusion evaluates 1 + 2 as the integer 3 and passes it to the function. The function converts it to a string and surrounds the string in literal quotation marks: "3".
The following example can help you understand the Evaluate
function and how it works with ColdFusion variable processing:
<cfset myVar2="myVar">
<cfset myVar="27/9"> <cfoutput> #myVar2#<br> #myVar#<br> #Evaluate("myVar2")#<br> #Evaluate("myVar")#<br> #Evaluate(myVar2)#<br> #Evaluate(myVar)#<br> </cfoutput>
The following table describes how ColdFusion processes this code:
As you can see, using dynamic expressions can result in substantial expression evaluation overhead, and the code can be confusing. Therefore, you should avoid using dynamic expressions wherever a simpler technique, such as using indexed arrays or structures can serve your purposes.
Using the Evaluate
function increases processing overhead, and in most cases it is not necessary. The following sections provide examples of cases where you might consider using the Evaluate
function.
You might be inclined to use the Evaluate
function in code such as the following:
<cfoutput>1 + 1 is #Evaluate(1 + 1)#</cfoutput>
Although this code works, it is not as efficient as the following code:
<cfset Result = 1 + 1>
<cfoutput>1 + 1 is #Result#</cfoutput>
This example shows how you can use an associative array reference in place of an Evaluate
function. This technique is powerful because:
The following example uses the Evaluate
function to construct a variable name:
<cfoutput>
Product Name: #Evaluate("Form.product_#i#")# </cfoutput>
This code comes from an example where a form has entries for an indeterminate number of items in a shopping cart. For each item in the shopping cart there is a product name field. The field name is of the form product_1, product_2, and so on, where the number corresponds to the product's entry in the shopping cart. In this example, ColdFusion does the following:
Evaluate
function, which does the remaining steps.
The following example has the same result as the preceding example and is more efficient:
<cfoutput>
ProductName: #Form["product_" & i]# </cfoutput>
In this code, ColdFusion does the following:
This code format does not use any dynamic evaluation, but it achieves the same effect, of dynamically creating a structure reference by using a string and a variable.
You can avoid using the SetVariable function by using a format such as the following to set a dynamically named variable. For example, the following lines are equivalent:
<cfset SetVariable("myVar" & i, myVal)>
<cfset "myVar#i#" = myVal>
In the second line, enclosing the myVar#i# variable name in quotation marks tells ColdFusion to evaluate the name and process any text in pound signs as a variable or function. ColdFusion replaces the #i# with the value of the variable i, so that if the value of i is 12, this code is equivalent to the line
<cfset myVar12 = myVal>
For more information on this usage, see "Using pound signs to construct a variable name in assignments".
The IIF
function is a shorthand for the following code:
<cfif argument1>
<cfset result = Evaluate(argument1)>
<cfelse> <cfset result = Evaluate(argument2)>
</cfif>
The function returns the value of the result variable. It is comparable to the use of the JavaScript and Java ? : operator, and can result in more compact code. As a result, the IIF
function can be convenient even if you are not using dynamic expressions.
The IIF
function requires the DE
function to prevent ColdFusion from evaluating literal strings, as the following example shows:
<cfoutput>
#IIf(IsDefined("LocalVar"), "LocalVar", DE("The variable is not defined."))# </cfoutput>
If you do not enclose the string "The variable is not defined." in a DE
function, the IIF
function tries to evaluate the contents of the string as an expression and generates an error (in this case, an invalid parser construct error).
The IIF
function is useful for incorporating ColdFusion logic in-line in HTML code, but it entails a processing time penalty in cases where you do not otherwise need dynamic expression evaluation.
The following example shows using IIF
to alternate table row background color between white and gray. It also shows the use of the DE
function to prevent ColdFusion from evaluating the color strings.
<cfoutput>
<table border="1" cellpadding="3"> <cfloop index="i" from="1" to="10"> <tr bgcolor="#IIF( i mod 2 eq 0, DE("white"), DE("gray") )#"> <td> hello #i# </td> </tr> </cfloop> </table> </cfoutput>
This code is more compact than the following example which does not use IIF
or DE
.
<cfoutput>
<table border="1" cellpadding="3"> <cfloop index="i" from="1" to="10"> <cfif i mod 2 EQ 0> <cfset Color = "white"> <cfelse> <cfset Color = "gray"> </cfif> <tr bgcolor="#color#"> <td> hello #i# </td> </tr> </cfloop> </table> </cfoutput>
The following example dynamically creates and manipulates variable names without using dynamic expression evaluation by using associative array notation.
You need to dynamically generate variable names in applications such as shopping carts, where the required output is dynamically generated and variable. In a shopping cart, you do not know in advance the number of cart entries or their contents. Also, because you are using a form, the action page only receives Form variables with the names and values of the form fields.
The following example shows the shopping cart contents and lets you edit your order and submit it. To simplify things, the example automatically generates the shopping cart contents using CFScript instead of having the user fill the cart. A more complete example would populate a shopping cart as the user selected items. Similarly, the example omits all business logic for committing and making the order.
<html> <head> <title>Shopping Cart</title> </head> <cfscript> CartItems=4; Cart = ArrayNew(1); for ( i=1; i LE cartItems; i=i+1) { Cart[i]=StructNew(); Cart[i].ID=i; Cart[i].Name="Product " & i; Cart[i].SKU=i*100+(2*i*10)+(3*i); Cart[i].Qty=3*i-2; } </cfscript> <body> Your shopping cart has the following items.<br> You can change your order quantities.<br> If you don't want any item, clear the item's check box.<br> When you are ready to order, click submit.<br> <br> <cfform name="ShoppingCart" action="ShoppingCartAction.cfm" method="post"> <table> <tr> <td>Order?</td> <td>Product</td> <td>Code</td> <td>Quantity</td> </tr> <cfloop index="i" from="1" to="#cartItems#"> <tr> <cfset productName= "product_" & Cart[i].ID> <cfset skuName= "sku_" & Cart[i].ID> <cfset qtyname= "qty_" & Cart[i].ID> <td><cfinput type="checkbox" name="itemID" value="#Cart[i].ID#" checked> </td> <td><cfinput type="text" name="#productName#" value="#Cart[i].Name#" passThrough = "readonly = 'True'"></td> <td><cfinput type="text" name="#skuName#" value="#Cart[i].SKU#" passThrough = "readonly = 'True'"></td> <td><cfinput type="text" name="#qtyName#" value="#Cart[i].Qty#"> </td> </tr> </cfloop> </table> <input type="submit" name="submit" value="submit"> </cfform> </body> </html>
The following table describes the code:
<html> <head> <title>Your Order</title> </head> <body> <cfif isDefined("Form.submit")> <cfparam name="Form.itemID" default=""> <cfoutput> You have ordered the following items:<br> <br> <cfloop index="i" list="#Form.itemID#"> ProductName: #Form["product_" & i]#<br> Product Code: #Form["sku_" & i]#<br> Quantitiy: #Form["qty_" & i]#<br> <br> </cfloop> </cfoutput> </cfif> </body> </html>
The following table describes the code: