Dynamic expressions and dynamic variables

Many ColdFusion programmers never encounter or require 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.
ColdFusion also includes an IIf function, which is most often used without dynamic expressions. This function dynamically evaluates its arguments, and you often must use the DE function to prevent the evaluation. For more information on using the IIF function, see Using the IIF function in this page.

About dynamic variables

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.

About dynamic expressions and 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:

  1. Takes a string expression and treats it as a standard expression, as if the expression was not a string.

  2. Parses the expression to determine the elements of the expression and validate the expression syntax.

  3. Evaluates the expression, looking up any variables and replacing them with their values, calling any functions, and performing any required operations.

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 Using Arrays and Structures.

Selecting how to create variable names

The following two examples describe cases when you need dynamic variable names:

  • Form applications where the number and names of fields on the form vary dynamically. In this case, the form posts only the names and values of its fields to the action page. The action page does not know all the names of the fields, although it does know how the field names (that is, the variable names) are constructed.
  • If the following are true:
    • ColdFusion calls a custom tag multiple times.
    • The custom tag result must be returned to different variables each time.
    • The calling code can specify the variable in which to return the custom tag result.
      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, first ensure that one of the following techniques cannot serve your purpose:
  • An array (using index variables)
  • Associative array references containing expressions to access structure elements
  • Dynamically generated variable names

Dynamic variable naming without dynamic evaluation

While ColdFusion does not always allow you to construct a variable name inline from variable pieces, but it does allow the most common uses.

Using number signs to construct a variable name in assignments

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 "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 prodNo = 12>
<cfset myArray[prodNo] = "Widget">

Dynamic variable limitation

When you use a dynamic variable name in quotation marks 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 productClassNo = 1>
<cfset productItemNo = 9>
<cfset "myArray[#productClassNo##productItemNo#]" = "Widget">

However, you can construct an array index value dynamically from variables without using quotation marks 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">

Dynamically constructing structure references

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.) 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:

<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].

Using dynamic evaluation

Dynamic evaluation and dynamic expressions have several features and consideratons.

ColdFusion dynamic evaluation functions

The following table describes the functions that perform dynamic evaluation and are useful in evaluating dynamic expressions:

Function

Purpose

DE

Escapes any double-quotation marks in the argument and wraps the result in double-quotation marks. The DE function is particularly useful with the IIF function, to prevent the function from evaluating a string to be output. For an example of using the DE function with the IIF function, see Using the IIF function.

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.

Operates identically to the Evaluate function, except that it can calculate arbitrary precision decimal arithmetic. If one or more operands in an arithmetic expression are decimal numbers, such as 12947834.986532, and are too long to be represented exactly by a ColdFusion numeric data type, the function uses arbitrary-precision arithmetic to calculate the  result,  and return the result as an arbitrarily long string of numbers. For more information about this function, see PrecisionEvaluate in the CFML Reference.

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.

Function argument evaluation 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:

  1. Evaluates the expression "1" & "2" as the string "12".

  2. Passes the string "12" (without the quotation marks) to the DE function.

  3. Calls the 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".

About the Evaluate function

The Evaluate function takes one or more string expressions, dynamically evaluates their contents as expressions from left to right, and returns the result of evaluating the rightmost argument.
The following example shows the Evaluate function and how it works with ColdFusion variable processing:

<cfset myVar="27/9">
<cfoutput>
  #myVar2#<br>
  #myVar#<br>
  #Evaluate("myVar2")#<br>
  #Evaluate("myVar")#<br>
  #Evaluate(myVar2)#<br>
  #Evaluate(myVar)#<br>
</cfoutput>

Reviewing the code

The following table describes how ColdFusion processes this code:

Code

Description

myVar2="myVar"><cfset myVar="27/9">

Sets the two variables to the following strings:myVar27/9

<cfoutput>#myVar2#

<br/>#myVar#<br/>

Displays the values assigned to the variables, myVar and 27/9, respectively.

#Evaluate("myVar2")#

<br>

Passes the string "myvar2" (without the quotation marks) to the Evaluate function, which does the following:1-Evaluates it as the variable myVar2. 2-Returns the value of the myVar2 variable, the string "myvar" (without the quotation marks).

#Evaluate("myVar")#

<br>

Passes the string "myvar" (without the quotation marks) to the Evaluate function, which does the following:1- Evaluates it as the variable myVar. 2-Returns the value of the myVar variable, the string "27/9" (without the quotation marks).

#Evaluate(myVar2)#

<br>

Evaluates the variable myVar2 as the string "myVar" and passes the string (without the quotation marks) to the Evaluate function. The rest of the processing is the same as in the previous line.

#Evaluate(myVar)#

<br/></cfoutput>

Evaluates the variable myVar as the string "27/9" (without the quotation marks), and passes it to the Evaluate function, which does the following:1- Evaluates the string as the expression 27/9.2-Performs the division.3-Returns the resulting value, 3

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.

Avoiding the Evaluate function

Using the Evaluate function increases processing overhead, and in most cases it is not necessary. These examples show some cases where you can consider using the Evaluate function:

Example 1

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:

<cfoutput>1 + 1 is #Result#</cfoutput>

Example 2

This example shows how you can use an associative array reference in place of an Evaluate function. This technique is powerful because:

  • Most ColdFusion scopes are accessible as structures.
  • You can use ColdFusion expressions in the indexes of associative array structure references. For more information on using associative array references for structures, see Structure notation.
    The following example uses the Evaluatefunction to construct a variable name:

This code comes from an example where a form has entries for an indeterminate number of items in a shopping cart. A product name field exists for each item in the shopping cart. The field name is of the form product_1, product_2, and so on, where the number corresponds to the product entry in the shopping cart. In this example, ColdFusion does the following:

  1. Replaces the variable i with its value, for example 1.

  2. concatenates the variable value with "Form.product_", and passes the result (for Form.product_1) to the Evaluate function, which does the remaining steps.

  3. Parses the variable product_1 and generates an executable representation of the variable. Because ColdFusion must run its parser, this step requires substantial processing, even for a simple variable.

  4. Evaluates the representation of the variable, for example as "Air popper".

  5. Returns the value of the variable.
    The following example has the same result as the preceding example and is more efficient:

    Product Name: #Evaluate("Form.product_#i#")#
    </cfoutput>

    In this code, ColdFusion does the following:

  6. Evaluates the expression in the associative array index brackets as the string "product_" concatenated with the value of the variable i.

  7. Determines the value of the variable i; 1.

  8. Concatenates the string and the variable value to get product_1.

  9. Uses the result as the key value in the Form structure to get Formproduct_1. This associative array reference accesses the same value as the object.attribute format reference Form.product_1; in this case, Air popper.
    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.

SetVariable function considerations

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 "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 number 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 number signs to construct a variable name in assignments in this page.

Using the IIF function

The IIf function is a shorthand for the following code:

<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:

#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.

<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:

<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>

Example: a dynamic shopping cart

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.

Create the form

  1. Create a file in your editor.
<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>
  1. Save the file as ShoppingCartForm.cfm.

Reviewing the code

The following table describes the code:

Code

Description

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>

Create a shopping cart as an array of structures, with each structure containing the cart item ID, product name, SKU number, and quantity ordered for one item in the cart. Populate the shopping cart by looping CartItems times and setting the structure variables to arbitrary values based on the loop counter. A real application would set the Name, SKU, and Quantity values on other pages.

<tr>
<td>Order?</td>
<td>Product</td>
<td>Code</td>
<td>Quantity</td>
</tr>

Start the form and its embedded table. When the user clicks the submit button, post the form data to the ShoppingCartAction.cfm page. The table formats the form neatly. The first table row contains the column headers. Each following row has the data for one cart item.

<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>

Loop through the shopping cart entries to generate the cart form dynamically. For each loop, generate variables used for the form field name attributes by appending the cart item ID (Carti.ID) to a field type identifier, such as "sku_". Use a single name, "itemID", for all check boxes. This way, the itemID value posted to the action page is a list of all the check box field values. The check box field value for each item is the cart item ID.Each column in a row contains a field for a cart item structure entry. The passthrough attribute sets the product name and SKU fields to read only; note the use of single-quotation marks. (For more information on the cfinput tag passthrough attribute, see the CFML Reference.) The check boxes are selected by default.

</cfform>

Create the Submit button and end the form.

Create the Action page

  1. Create a file in your editor.
  2. Enter the following text:

<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>
Quantity: #Form["qty_" & i]#<br>
<br>
</cfloop>
</cfoutput>
</cfif>
</body>
</html>
  1. Save the file as ShoppingCartAction.cfm
  2. Open ShoppingCartform.cfm in your browser, change the check box and quantity values, and click Submit.

Reviewing the code

The following table describes the code:

Code

Description

<cfif

isDefined("Form.submit")>

Run the CFML on this page only if it is called by submitting a form. This is not needed if the form and action pages are separate, but is required if the form and action page were one ColdFusion page.

<cfparam

name="Form.itemID"

default="">

Set the default Form.itemID to the empty string. This prevents ColdFusion from displaying an error if the user clears all check boxes before submitting the form (so no product IDs are submitted).

You have ordered the following items:<br>
<cfloop index="i" list="#Form.itemID#">
ProductName: #Form["product_" & i]#<br>
Product Code: #Form["sku_" & i]#<br>
Quantity: #Form["qty_" & i]#<br>
<br>
</cfloop>
</cfoutput>
</cfif>

Display the name, SKU number, and quantity for each ordered item.The form page posts Form.itemID as a list containing the value attributes of all the check boxes. These attributes contain the shopping cart item IDs for the selected cart items. Use the list values to index a loop that outputs each ordered item.Use associative array notation to access the Form scope as a structure and use expressions in the array indexes to construct the form variable names. The expressions consist of a string containing the field name's field type prefix (for example, "sku_"), concatenated with the variable i, which contains the shopping cart ItemID number (which is also the loop index variable).

 Adobe

Получайте помощь быстрее и проще

Новый пользователь?