This has been one of the biggest obstacles in teaching new people ColdFusion.
When to use # is ambiguous at best. Since using them doesn't often create a problem it seems that most people gravitate to using them too much.
So, what are the basic rules?
I think it may be easier to say where NOT to use #. The only place is in cfif statements, and cfset statements where you are not using a variable to build a string in quotes. You would need to use the # sign in almost all other cases.
Example of where you are not going to use it:
<cfset value1 = 5>
<cfset value2 = value1/>
<cfif value1 EQ value2>
Yay!!!
</cfif>
<cfset value2 = "Four plus one is " & value1/>
Examples of where you will use #:
in a cfset where the variable is surrounded by quotes
<cfset value1 = 5>
<cfset value2 = "Four plus one is #value1#"/>
the bodies of cfoutput, cfmail, and cffunction (output="yes") tags
<cfoutput>#value2#</cfoutput>
<cfmail to="e#example.com" from="e#example.com" subject="x">#value2#</cfmail>
<cffunction name="func" output="yes">#value2#</cffunction>
in an attribute value of any coldfusion tag
<cfset dsn = "myDB"/>
<cfquery name="qryUsers" datasource="#dsn#">
<cfset value1 = 5>
<cfset value2 = 10/>
<cfloop from="#value1#" to="#value2#" index="i">
<cfqueryparam value="#value1#" cfsqltype="cf_sql_integer"/>
EDIT:
One oddball little thing I just noticed that seems inconsistent is conditional loops allow the variable name to be used with and without # signs.
<cfset value1 = 5>
<cfloop condition = "value1 LTE 10">
<cfoutput>#value1#</cfoutput><br>
<cfset value1 += 1>
</cfloop>
<cfset value1 = 5>
<cfloop condition = "#value1# LTE 10">
<cfoutput>#value1#</cfoutput><br>
<cfset value1 += 1>
</cfloop>
Here's what Adobe has to say about it:
Using number signs
String interpolation:
<cfset name = "Danny" />
<cfset greeting = "Hello, #name#!" />
<!--- greeting is set to: "Hello, Danny!" --->
Auto-escaped string interpolation in cfquery:
<cfset username = "dannyo'doule" ?>
<cfquery ...>
select u.[ID]
from [User] u
where u.[Username] = '#username#'
</cfquery>
<!--- the query is sent to the server (auto-escaped) as: --->
<!--- select u.[ID] from [User] u where u.[Username] = 'dannyo''doule' --->
<!--- note that the single-quote in the username has been escaped --->
<!--- by cfquery before being sent to the database server --->
Passing complex arguments/attributes in CFML:
<cfset p = StructNew() />
<cfset p.firstName = "Danny" />
<cfset p.lastName = "Robinson" />
<cfmodule template="modules/view/person.cfm" person="#p#">
<!--- the variable Attributes.person will be --->
<!--- available in modules/view/person.cfm --->
Passing complex arguments requires # signes only in CFML, not CFScript. In addition, you can pass any kind of value: simple values, arrays, structs, cfcomponents, cffunctions, java objects, com objects, etc.
In all these cases, the text between the # signs does not have to be the name of a variable. In fact, it can by any expression. Of course, for string interpolation, the expression must evaluate to a simple value, but for argument/attribute passing in CFML, the expression may evaluate to any complex value as well.
The #...# syntax allows you to embed an expression within a string literal. ColdFusion is unfortunately pretty inconsistent about what's a string and what's an expression. Jayson provided a good list of examples of when to use (or not use) #s.
At the risk of sounding like a wise-guy, a rule of thumb is: use # around variables or expressions only when not doing so doesn't yield the correct result. Or: if you don't need them, don't use them.
I like Jayson's answer though.
Let's start by assuming you aren't talking about cfoutput tags, cause then the answer is always, elsewhere in your code, if you are inside of quotation marks, then need to use # symbols if it's possible to actually type the value that is going to be used...so if you are in a cfloop tag setting the 'to' attribute, you could easily type 6, but if you want to use a variable you need to use the # symbols. Now if you are in a cfloop tag setting the query parameter, there is no way you could actually type the query into that attribute, there is no way to type a query, so no # symbols are needed.
Likewise in a cfdump tag, you can dump static text, so if you want to dump the contents of a variable, then you will need to use a # symbol. This problem is generally self-correcting, but I feel your pain, your students are probably frustrated that there is no "ALWAYS USE THEM" or "NEVER USE THEM" approach...sadly this isn't the case, the only thing that is true, is only one way inside of quotation marks is going to be correct. So if it isn't working look at it hard and long and think to yourself: "Could I type that value out instead of using the value contained in that variable?" If the answer is no, then the # symbols won't be needed, otherwise get your # character foo on.
Related
I'm setting up something to allow me to masquerade as another user in my system. But, I want to use listFind instead of a compare() method.
<cfif compare(session.userName, "userOne") EQ 0>
<cfset #session.userName# = "userThree">
</cfif>
In the above statement, I'm trying to conform it to a listFind, where if userOne is currently logged in, then set the session.userName to userThree. But, I'm having trouble.
What I have thus far...
<cfif #ListFind("userOne, UserTwo")#>
<cfset #session.userName# = "userThree">
</cfif>
You will need to provide two independent arguments to ListFind. At the moment you are providing one string. The list is also the first arguments and string search for is the second.
<cfif ListFind("userOne", session.userName)>
<cfset session.userName = "userThree">
</cfif>
On a side note, the hashes are only necessary for string interpolation. In other words, only when you want a variable/expression to be evaluated and inserted into a string.
I've tried everything I've found that might be applicable to this, without success to date.
I have variables that look like
x000Foo
I'm trying to create one of these dynamically in form scope from query results and have tried the following, and a few others, without success:
<cfloop query="qFormFields">
<cfset "form.x000#fieldname#" = 0>
You have attempted to dereference a scalar variable of type class coldfusion.sql.QueryColumn as a structure with members
<cfset "form.x[000]#fieldname#" = 0>
The value x000AA_report cannot be converted to a number.
<cfset form["x000#fieldname#"] = 0>
The value x000AA_report cannot be converted to a number.
</cfloop
I know it's related to the zeros, but I'm not sure how to get around it without resorting to renaming these variables throughout the application.
I'm on ColdFusion2016
I'm not sure if this is what you're trying to do: but here's how you can do dynamic variables:
<cfset fieldname = "foo">
<cfset form["x000" & fieldname] = 0>
<cfdump var="#form#">
<!--- variable form.x000Foo = 0 --->
Runnable example on TryCF.com
you can try evaluate function read more
<cfloop query="qFormFields">
<cfset fieldvalue = Evaluate("form.x000#fieldname#")>
<cfdump var="#fieldvalue#">
</cfloop>
let me know if it is working or not.
I have a list which I want to split out and insert into a table. The list contains paired names and values:
R0006^^1.00000000~~R0042^^1.00000000~~R0049^^1.00000000~~R0072^^1.00000000~~R0088^^3.00000000~~R0092^^1.00000000~~R0106^^1.00000000
How can I loop over this list and insert the names and values into a database table as I am struggling to get in my head the use of different delimiters and their associated values.
Many Thanks
JC
ColdFusion's tags & functions don't fully deal with multi-character delimiters very well. ArrayToList() supports multiple delimiters but most most other list-related functions do not.
If your data never contains a ~ or ^ by itself, I would take advantage of that and replace the 2-length delimiters with one-length delmiters.
(Edit: As Leigh points out in comments, a ReReplace, or ReplaceList() is not needed in this case as CF ignores empty elements by default). It won't change the output to remove it, but that's the point, having it there isn't doing anything useful either. Commented out for clarity of point.)
<cfset dList = "R0006^^1.00000000~~R0042^^1.00000000~~R0049^^1.00000000~~R0072^^1.00000000~~R0088^^3.00000000~~R0092^^1.00000000~~R0106^^1.00000000" />
<!---cfset dList = ReReplace(dList,"(~|\^)\1","\1","ALL")--->
<cfset dArray = ListToArray(dList,"~",false) />
<cfloop array="#dArray#" index="a1">
<cfquery...>
insert into mytable(lname,lvalue)
values(<cfqueryparam value="#listfirst(a1,"^")#">,<cfqueryparam value="#listlast(a1,"^^")#">)
</cfquery>
</cfloop>
The nice part about this is that it has pretty good backwards compatibility as well.
However, this does assume that each item in the ~ delimited list has two sub-items. If it does not, and only has the field label, you can do this.
<cfset dList = "R0006^^1.00000000~~R0042^^1.00000000~~R0049^^1.00000000~~R0072^^1.00000000~~R0088^^3.00000000~~R0092^^1.00000000~~R0106^^1.00000000" />
<!---cfset dList = ReReplace(dList,"(~|\^)\1","\1","ALL")--->
<cfset dArray = ListToArray(dList,"~",false) />
<cfloop array="#dArray#" index="a1">
<cfquery...>
insert into mytable(lname,lvalue)
values(<cfqueryparam value="#listfirst(a1,"^")#">,<cfqueryparam value="#(listlen(a1,"^") gt 1 ? listlast(a1,"^") : "")#">)
</cfquery>
</cfloop>
Finally, as David Faber points out in the comments, you can use ReplaceList(dlist, "~~,^^", "~,^") instead of ReReplace(dList,"(~|\^){2}","\1","ALL") which will achieve the same goal but has the added benefit of being easier to read for people who may not be comfortable with Regular Expressions.
I would take a slightly different approach (for the reasons stated in my comments ... what if the character sequences ~^ or ^~ exist in the data, or even the single characters ^ or ~?) and turn the list into JSON, then deserialize it into a struct:
<cfset the_list = "R0006^^1.00000000~~R0042^^1.00000000~~R0049^^1.00000000~~R0072^^1.00000000~~R0088^^3.00000000~~R0092^^1.00000000~~R0106^^1.00000000" />
<!--- Escape characters that need to be escaped --->
<cfset the_list = replace(the_list, "\", "\\", "all") />
<cfset the_list = replace(the_list, """", "\""", "all") />
<cfset the_list = replace(the_list, "^^", """:""", "all") />
<cfset the_list = "{""" & replace(the_list, "~~", """,""", "all") & """}" />
<cfset the_coll = deserializeJSON(the_list) />
The only difficulty with the above would be if there were duplicate keys. In that case one might use an array of structs - this can be accomplished simply by changing the line replacing the double tilde ~~:
<cfset the_list = "[{""" & replace(the_list, "~~", """},{""", "all") & """}]" />
Then loop over the array to insert into the database.
I have an html form that collects variables that look like:
ent1_label
ent2_label
ent1att1_label
ent1att2_label
ent2att1_label
ent2att2_label
ent1att1val1_label
ent1att1val2_label
...
ent2att2val2_label
I don't know in advance how many of ent, att, or val will be coming from the form to the action script. In the input form, I run loops that concatenate the variable names:
ent<cfoutput>#i#</cfoutput>att<cfoutput>#j#</cfoutput>val<cfoutput>#k#</cfoutput>_label
(where i, j, and k are the number of ent, att, and val)
This works great on the input form, but then I'm lost on how to refer to them in the action script.
I've played around with #form.fieldnames# which has all of the actual variable names.
<cfset #formfields# = listToArray(#form.fieldnames#, ",")>
<cfset #formlength# = arraylen(#formfields#)>
<cfset #entattval_label# = arrayNew(1)>
<cfloop from="1" to="#formlength#" index="i">
<cfif REfind("ENT[0-9]*ATT[0-9]*VAL[0-9]*_LABEL", #formfields[i]#) EQ 1>
<cfset arrayAppend(entattval_label, "#formfields[i]#")>
</cfif>
</cfloop>
will make separate arrays for each subset of variables I need. But how do I make it print the contents of the variables, instead of their names?
<cfset #label_length# = arraylen(#entattval_label#)>
<cfloop from="1" to="#label_length#" index="i">
<cfoutput>#entattval_label[i]#</cfoutput>
</cfloop>
You can reference a variable with a dynamic name using what's called array notation:
foo.bar can also be written as foo['bar'] - even though it's a structure and not an array.
This means that you can inject any dynamic name into your reference that you like; and this also works for built in variable scopes like form, url, etc:
For example: form['ent#i#att#j#val#k#_label'] where i, j, and k are the integers you need.
If you find that hard to read, you could also write it as:
form['ent' & i & 'att' & j & 'val' & k & '_label']
In these situations, if the form is being expanded on the client-side (e.g. with JavaScript) without server involvement, I often find it's easiest to include a numeric hidden field (or in your case, maybe 3?) to indicate the ranges for i, j, and k.
(Too long and too much formatting for an easily-readable comment.)
Just for the record, you can significantly shorten your code by using ReMatchNoCase() instead of ReFind() if you're using CF8+ or Railo.
ReMatchNoCase (and ReMatch of course) looks for as many matches as possible. I created a form.fieldnames variable for demonstration but that's not even really needed
<cfset form.fieldnames = "ent1_label,ent2_label,notvalid,btnSelect,ent1att1_label,ent1att2_label,ent2att1_label,ent2att2_label,ent1att1val1_label,ent1att1val2_label,ent2att2val2_label">
<Cfset NamesArray = REMatchNoCase("ENT[0-9]*ATT[0-9]*VAL[0-9]*_LABEL",form.fieldnames)>
<cfoutput><cfloop from="1" to="#ArrayLen(NamesArray)#" index="i">
#NamesArray[i]#: #form[NamesArray[i]]#<br />
</cfloop>
</cfoutput>
I currently have a coldfusion regex that checks whether a string is alphanumeric or not.
I would like to open that up a bit more to allow period and underscore characters. How would I modify this to allow that?
<cfset isValid= true/>
<cfif REFind("[^[:alnum:]]", arguments.stringToCheck, 1) GT 0>
<cfset isValid= false />
</cfif>
Thanks
No need for cfif - here's a nice concise way of doing it:
<cfset isValidString = NOT refind( '[^\w.]' , Arguments.StringToCheck )/>
Alternatively, you can do it this way:
<cfset isValidString = refind( '^[\w.]*$' , Arguments.StringToCheck ) />
(To prevent empty string, change * to +)
This method can make it easier to apply other constraints (e.g. must start with a letter, etc), and is a slightly more straight-forward way of expressing the original check anyway.
Note that the ^ here is an anchor meaning "start of line/string" (with $ being the corresponding end), more information here.
This should do it.
<cfset isValidString= true/>
<cfif REFind("[^[:alnum:]_\.]", arguments.stringToCheck, 1) GT 0>
<cfset isValidString= false />
</cfif>
Also using "isValid" for a variable name is not a great practice. It is the name of a function in ColdFusion and could cause you issues someday.
Would this work for you?
refind("[\w\d._]","1234abcd._")