How to populate a template string in CFML - coldfusion

Forgive the newbie question here, but I am trying to allow for custom variables in a string that I would pull from a query, and replace those with results of another query. So to simplify it, I would need something like this...
<cfset mystring = "This is my %firstname% and this is my %lastname%.">
Then suppose I had query results of MyQuery,first and MyQuery.last, I would like the most efficient way to replace the %firstname% and %lastname% in mystring with the query results.
Any help/example would be so appreciated!
Thanks.

Are you looking for replaceNoCase()?
<cfset myNewString = replaceNoCase(myString, '%firstname%', myQuery.first, 'all')>
<cfset myNewString = replaceNoCase(myNewString, '%lastname%', myQuery.last, 'all')>

if you had a query like this:
<cfquery name="myquery" datasource="mydatasource">
select firstname, lastname from users
</cfquery>
then you would out put the values from that by doing the following:
<cfoutput query="myquery">
<cfset variables.mystring = "This is my #firstname# and this is my #lastname#.">
</cfoutput>

If you are using Lucee then you can take advantage of replace(string, struct) (showing cfscript syntax):
template = "This is my %firstname% and this is my %lastname%.";
replaceMap = {
"%firstname%" : "John"
,"%lastname%" : "Doe"
}
populated = replace(template, replaceMap); // use replaceNoCase() for case-insensitivity
All of the replacements take place in the Java code which is much more efficient than doing it in a CFML loop.
Also, replace() is much more efficient than replaceNoCase(), so if the CaSe is known in advance you should use replace() where possible, or consider normalizing the case, e.g. with lcase(), prior to calling replace().

Related

Why does this function not return a string that will work ListToArray or ValueList?

I have created a function within a format.cfc component that returns a string without any HTML code in it:
<cffunction name="RemoveHTML" access="public" returntype="string" output="false" hint="Returns a string without any html">
<cfargument name="UserString" required="yes">
<cfset var result = "#REReplaceNoCase(Canonicalize(ARGUMENTS.UserString,false,true),'<[^>]*(?:>|$)', '', 'ALL')#">
<cfreturn result>
</cffunction>
I now want to split the string at each space and convert it into a list. So I tried using ValueList() and ListToArray() but they don't like the value returned from the function.
Using ValueList() I get an error saying:
Complex constructs are not supported with function ValueList
Or I get this error when using ListToArray:
Complex object types cannot be converted to simple values
I'm basically just doing this:
<!--- ValueList() --->
<title>#ValueList(Application.Format.RemoveHTML(UserString = rsProduct.Title), ' ')#</title>
<!--- ListToArray() --->
<title>#ListToArray(Application.Format.RemoveHTML(UserString = rsProduct.Title), ' ')#</title>
If I remove the ListToArray() or ValueList() function then I get back what I expect - a product title string with no HTML in it.
So why is the function not returning a string even though it looks like one? Or am I missing something completely obvious?
As others have noted in the comments, ValueList is designed to return a list of values that are contained in a column of a query object. It won't work with a string value.
ListToArray converts a list to an array. You can't then output an array in your HTML. So ListToArray is working fine, it's when you try to display it in a cfoutput that the error occurs.
It's a good idea to use the in-built encoding functions in CF, for example encodeForHTML. So you can do something like:
<title>#encodeForHTML(Application.Format.RemoveHTML(UserString = rsProduct.Title))#</title>
encodeForHTML, can accept an optional boolean 2nd argument (which is false by default), to indicate if you want to canonicalize the string. So you may want to do that instead of calling Canonicalize in your custom RemoveHTML function. After all your function is called RemoveHTML not RemoveHTMLAndCanonicalize :)
update
In response to OP's comment.
To get a comma delimited list from your 'space delimited' string, then you can use the replace function. Something like:
<title>#encodeForHTML(replace(RemoveHTML(rsProduct.Title), " ", ",", "all"))#</title>
You can of course, put the replace inside your custom function, I'm just demonstrating how it works.
You'll need to be aware, that it'll replace all spaces with a comma, so if you have 2 or more spaces in a row then it'll show ,, (depending on how many spaces). To get around that you can use a regular expression like so:
<title>#encodeForHTML(reReplace(RemoveHTML(rsProduct.Title), " +", ",", "all"))#</title>
You can also use listChangeDelims intead of reReplace as it ignores empty elements.
<title>#encodeForHTML(listChangeDelims(RemoveHTML(rsProduct.Title), ",", " "))#</title>
Personally I'd go with the regular expression version as it's more powerful, you'll want to wrap it up in a function though to keep the view nice and clean.
I think there are multiple ways to do this, consider below is my text
<cfset myText = "Samsung Galaxy Note 4"/>
first method by using simple replace function
<cfset firstSolution = replace(myText," ",",","all")/>
<cfdump var="#firstSolution#" />
Second method by using reReplace method
<cfset secondSolution = rEReplace(myText,"\s+",",","all")/>
<cfdump var="#secondSolution#" />
If I would be you I would have use second method cause if by any chance I have multiple spaces in my string then instead of getting multiple ',' I will get single ',' given string is used in the title of the page, I would not take any risk have incorrect title.

Coldfusion: How to split a string into a set of variables

I'm trying to teach myself ColdFusion.
I have a string coming in from the database in this format:
domain.com
<br/>
www.facebook.com/facebookpage
<br/>
http://instagram.com/instagrampage
It is all coming from #getRacer.txtDescription#. The format of this text will always be the same.
I need to split it into 3 variables. I tried this (derived from the example on the adobe website)
<h3>ListToArray Example</h3>
<cfset myList = ValueList(getRacer.txtDescription)>
<p>My list is a list with <cfoutput>#ListLen(myList)#</cfoutput> elements.
<cfset myArrayList = ListToArray(myList,'<br/>')>
<p>My array list is an array with
<cfoutput>#ArrayLen(myArrayList)#</cfoutput> elements.
I somehow ended up with 11 items in the array.
Thank you
This should work.
<cfset TestSTring = "domain.com<br/>www.facebook.com/facebookpage<br/>http://instagram.com/instagrampage">
<cfset a = TestString.Split("<br/>")>
The reason ListtoArray is displaying 11 items is because ColdFusion treats each character in the delimiter string (<br/>) as a separate delimiter
Based on #Leigh's comment updating my answer to ensure people should learn the Coldfusion APIs rather than fiddling with Java functions, <cfset a = ListToArray(TestString, "<br/>", false, true)> will also work. Thanks Leigh.
Note: The false at the end is for the includeEmptyFields flag and the true is for the multiCharacterDelimiter flag. See the docs.
<cfset myList = ReplaceNoCase(getRacer.txtDescription,'<br/>','|','ALL')>
<cfset myArrayList = ListToArray(myList,'|')>
I chose a pipe character because it is unlikely to already exist in your string. If you wanted to account for the possibility that your BR tag may or may not use XML syntax then you could you regex:
<cfset myList = ReReplaceNoCase(str,'<br/?>','|','ALL')>
<cfset myArrayList = ListToArray(myList,'|')>

Coldfusion Regex get strings between tags

We currently have some application variables being defined on our apps and also have a special set of tags that are processed within our templates. However I'm finding it to be a bit difficult to split the tags, so I can convert them to application variables when parsed by the template.
<cfset mystring = "[pss]'fetchMessages','VWO-Tracking-Code'[end_pss]">
<cfset the_match = REMatch("\[pss\]\s*(((?!\[pss\]|\[end_pss\]).)+)\s*\[end_pss\]",mystring) />
<cfdump var="#the_match#" />
Our goal is to split the strings between the "[pss] and [end_pss]"
The regex above simply takes the string and applies it to a CF array, which is all good. However I strictly want the code between it as I'll be able to convert it to
<cfset application.snippets['VWO-Tracking-Code']>
Right now it returns everything as one string and I also require the first portion to determine what type of functionality is required.
Any ideas on how to do this would be greatly appreciated.
We are currently using CF 8 and the structure of the code is exactly the same all the time. The only thing is it can be contained within other strings of code as well...
If you're only doing this for one set of tags you can use the replaceList function rather than regEx
<cfset mystring = "[pss]'fetchMessages','VWO-Tracking-Code'[end_pss]">
<cfset mystring = replaceList(mystring,"[pss],[end_pss],'",'')>
<cfset firstitem = listfirst(mystring)>
<cfset seconditem = listlast(mystring)>
<cfdump var="#firstitem#">
<cfdump var="#seconditem#">
This outputs fetchMessages and VWO-Tracking-Code. If you want/need the single quotes on your strings then you can remove the ,' from the 2nd parameter of the replaceList function.

RegExpression to replace text within tags

I have HTML code as String variable in Coldfusion.
For example:
<cfset str = "<span class='xyz'> sample text within span </span>" >
Now I want to repalce the word span from text "sample text within span" but not the tag name.
Can someone help me?
Thanks in advance.
I'm going to dumbly answer your question:
<cfset str = "<span class='xyz'> sample text within span </span>" >
<cfdump var="#str#" />
<!--- Convert to list based on start and end tag brackets --->
<cfset arr = listToArray(str, ">,<") />
<!--- Replace the ACTUAL text --->
<cfset newStr = replace(str, arr[2], "my new text") />
<cfdump var="#newStr#" />
Disclaimer: if I caught myself writing this I would probably think myself on the wrong track.
This road leads to one trying to use regular expressions to parse HTML, regarded as a bad thing as mentioned in this article.
Perhaps you can explain your problem a little more and we can help.
I habe changed my code like this and it seems to work, but how efficent it is I don't know
Can someone please check it?
<cfset htmlcontents = ReReplaceNoCase(htmlcontents, "(>[^<]*?)(#Lcase(text2replace)#)", "\1<span class=suchehighlight>\2</span>","ALL")>
Thanks

When should I use # in ColdFusion?

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.