I created a simple custom tag which takes a string, replaces whitespace with "-" and "&" with "and" for a querystring (I don't want %20's and the like).
Anyways, it works fine, however my custom tag is creating a space before itself like so:
qsEncode.cfm: (custom tag)
<cfparam name="attributes.string" type="string" default="">
<cfset whitespace = Replace(attributes.string," ","-","all")>
<cfset ampersand = Replace(whitespace,"&","and","all")>
<cfoutput>#ampersand#</cfoutput>
The implementation of the tag:
">#getCategory.Name#
And the final output which is creating a space before the tag:
somepage.cfm?Page=%20Finance-and-Taxes
My custom tag isn't being passed a string that has whitespace prepended to it (and even if it was it would be replaced by the "-" character) so I don't understand why the tag is creating whitespace.
Note: I do realize I can include ?Page= inside my custom tag which would fix it, but I'm still curious as to why this is happening.
Your specific problem with extra space can likely be fixed by using:
<cfsetting enablecfoutputonly="true">
as the very first thing inside your custom tag (and setting it back to "false" at the end).
However, I would highly recommend replacing the functionality of the custom tag with an actual function -- either inline, or (preferably) within a cfc. Either way, you want something like this:
<cffunction name="qsEncode" output="false" returntype="string">
<cfargument name="str" type="string" required="true">
<cfset var whitespace = Replace(arguments.str," ","-","all")>
<cfset var ampersand = Replace(whitespace,"&","and","all")>
<cfreturn ampersand>
</cffunction>
Then you'd have:
#getCategory.Name#
For utility functions like these that don't maintain state, a quick solution (putting aside arguments as to the wisdom of singletons) is to set up a utility cfc, and store it as a singleton in the application scope. Instantiate the utility.cfc in the onApplicationStart handler of your Application.cfc. Then, throughout your application, you can do things like this:
application.utility.qsEncode('this');
application.utility.someOtherFunction('that');
application.utility.yetAnotherStringMangler('theother');
Ken is correct - the white space in your custom tag CFM is creating the extra space in it's output. And I recommend his answer (I prefer calling UDF functions in over custom tag CFMs... even better as a function in a CFC object over CFM).
But if you want to simply adjust your existing custom tag, use CFSILENT to stifle whitespace output (and assure no trailing whitespace after last tag in the file).
<cfsilent>
<cfparam name="attributes.string" type="string" default="">
<cfset whitespace = Replace(attributes.string," ","-","all")>
<cfset ampersand = Replace(whitespace,"&","and","all")>
</cfsilent><cfoutput>#ampersand#</cfoutput>
Related
I recently moved my code from CF9 to CF11 and I am having issues when I am trying to use serializeJSON. According to CF docs:
Starting from ColdFusion 11, the data type is preserved during the
code execution time for Query and CFCs.
SerializeJSON considers datatypes defined in the database for
serialization. If the database defines a column as a string, any
number inserted into the column will still be treated as a string by
SerializeJSON.
But I guess this is not the case....
When I am pulling data out of a varchar column in CF9 it comes out like this "docid":"123" which is what I want but in CF11 the same data look like this "docid":123 and is causing an issue with what I am trying to do.
To be more specific, my ids look like this 2001101009460111385185 which is longer than what javascript can accept and they get converted into scientific notation. With the old format I didn't have this issue because my ids were treated as a string which is what I want.
Note: my code is exactly the same on both versions of CF
Anyone had this issue before and HOW did you go around it?
Code Sample
I am calling this function via an AJAX call, this function returns an array with a struct in it. When I dump the return value after I serialize the result I can see a JSON object in my console but the quotes are missing from all the number values. In a test file I created a simple query and then I am serializing the results and everything looks good......
<cffunction name="locationData" returnformat="json" access="remote">
<cfargument name="locationid" required="yes" type="string">
<cfargument name="clientBrandid" required="yes" type="string">
<cfscript>
locationData = new mod_sigweb.components.xamplifierCFCs.location_info();
result = locationData.getLocation(locationid,clientBrandid);
</cfscript>
<cfdump var="#serializeJSON(result[1],'struct')#">
<cfabort>
<cfreturn #result#>
</cffunction>
The unfortunate workaround that I have had to use for this bug is to concatenate a space to the end of the value
<cfloop index="i" from="1" to="#ArrayLen(result)#">
<cfset result[i].docid = result[i].docid & " "/>
</cfloop>
and then the js has to be aware of this (I know, it's bad) and remove that trailing space.
This question already has answers here:
coldfusion weird extra space
(3 answers)
Closed 8 years ago.
I have a CFC that returns a string containing part of a URL. I want to concatenate this to the end of the domain name of the site so it makes a fully qualified URL.
However, the ColdFusion is creating a space before the concatenation. Here is how my concatenation looks:
http://www.mywebsite.com#APPLICATION.MyCFC.GetURL(urlid = url.id)#
So we have two parts:
The domain part which is just http://www.mywebsite.com
The string that's returned from the CFC which is like this /products/20
However the final output ends up like this:
http://www.mywebsite.com /products/20
So for some reason it puts a space just before concatenating the string from the CFC. I have tried to put a Trim() around the CFC invokation but it doesn't do anything.
What I have also tried to do is put the string from the CFC in a variable like this <cfset myurl = #APPLICATION.MyCFC.GetURL(urlid = url.id)#. I then concatenated this variable to the domain like this: http://www.mywebsite.com/#url# and it works fine without adding any spaces.
Why is it doing this? I don't want to keep storing the output of the CFC in yet another local variable everytime I want to use it.
This is the code from the CFC (I've left out the database stuff that it does for sake of confidentially and clarity but its essentially just this):
<cffunction name="GetURL" access="public" returntype="string">
<cfargument name="urlid" required="yes">
<cfset var result="/products/#urlid#">
<cfreturn result>
</cffunction>
add output="false" to your <cffunction> (and <cfcomponent> if it is not an UDF) may solve your problem.
I'm working with ColdFusion Server version 8,0,0,176276.
I'm trying to add an autosuggested form field, with asynchronous population through a cfc. I'm using http://www.forta.com/blog/index.cfm/2007/5/31/coldfusion-ajax-tutorial-1-autosuggest for inspiration and syntax.
The autosuggest field works fine if I use a static query (as in Forta's first example). The cfc is successfully returning an array when not used in the form field.
But when I use the cfc for autosuggest, there are no suggestions provided.
I can't see the contents of the input field with "view source," but if I do "inspect element" on the field in Chrome, I can see a div with class="yui-ac-bd" and a ul under it. The list items in the ul are empty when using the cfc, whereas when I use a static query, the list items contain the array members.
Here's the code on my page:
<cfform>
<cfinput type="text" name="JobP"
autosuggest="cfc:autosuggest.AutoSuggest({cfautosuggestvalue})">
</cfform>
And here's autosuggest.cfc:
<cfcomponent output="false" >
<cffunction name="AutoSuggest" access="remote" returntype="array">
<cfargument name="ObjectType" required="false" default="JOBP">
<cfset var result=ArrayNew(1)>
<cfquery name="Objects" datasource="UC4MP">
SELECT oh_name
FROM uc4.oh
WHERE oh_otype = '#ObjectType#'
AND oh_deleteflag = 0
AND oh_lastdate > sysdate - 90
AND oh_client = 1000
and oh_name like 'A%'
ORDER BY oh_name
</cfquery>
<cfloop query="Objects">
<cfset ArrayAppend(result,oh_name)>
</cfloop>
<cfreturn result>
If I put the following code on my page, it outputs the array with the desired contents:
<cfinvoke component="autosuggest" method="AutoSuggest" returnVariable="result">
cfdump var="#result#">
I don't use jQuery yet; most of my Google results for CF autosuggest have involved jQuery and I haven't been able to wade through them for relevance to my problem. Just in case that was going to be your suggestion.
Thank you all for your advice! I had not known there was a separate ajax debugger, the output of which immediately made the problem clear:
info:http: Invoking CFC: /rd/autosuggest.cfc , function: AutoSuggest ,
arguments: {"ObjectType":"A"}
My cfc arguments didn't include the autosuggest itself, so the string passed to the input field was being interpreted as "ObjectType" (the first argument) and used in the query. Since there are no records where oh_otype = 'A', the result was always empty.
I updated my cfc's arguments to
<cfargument name="ObjectType" required="yes" default="JOBP">
<cfargument name="autosuggest" required="yes">
and the invocation to
<cfinput type="text" name="JobP"
autosuggest="cfc:autosuggest.AutoSuggest('JOBP',{cfautosuggestvalue})">
...works perfectly now.
If i remember correctly when I did this some years ago, I had to create a virtual directory in Apacahe and set permisions using the following directives. You can apply the same to IIS if you are working in that environment. That should get it going for you.
Alias /CFIDE "/opt/coldfusion9/wwwroot/CFIDE"
<Directory "/opt/coldfusion9/wwwroot/CFIDE">
allow from all
Options FollowSymLinks
Since then I have been using the typehead javascript from bootstrap 2. Much cleaner and easier to style. You can still use your cfc to call the data and I am including the code to clean it up for typehead.
<cfinvoke component="autosuggest" method="AutoSuggest" returnVariable="result">
<cfset mylist = ArrayToList(result, ",")>
<cfset mylist=ValueList(ShowKey.keyword)>
<cfset mylist = jSStringFormat(#mylist#)>
<input name="keyword" id="keyword" type="text" data-provide="typeahead" data-items="10" data-source='["<cfoutput>#replace(mylist,',','","','ALL')#</cfoutput>"]'/>
I'm a little in awe on how my first Cfmails are looking.
Problem is, I'm using variables for both text and content and I would still like to have some sort of spacing.
For example, if I have:
<cfprocessingdirective suppresswhitespace="No">
<cfmail
TO="#Local.User.email#"
FROM="..."
SERVER="..."
USERNAME="..."
PASSWORD="..."
WRAPTEXT="80"
SUBJECT="#tx_automailer_register_subject# - #Local.User.Comp#">
#tx_automailer_default_hello#
#tx_automailer_register_info#
#tx_automailer_register_iln#: #Local.User.iln#
#tx_firma#: #Local.User.firma#
#tx_ansprechpartner#: #Local.User.ansprechpartner#
#tx_adresse#: #Local.User.adresse#
#tx_plz#: #Local.User.plz#
#tx_ort#: #Local.User.ort#
...
The only place this looks nice is my cfc :-) In the mail itself everything is going bazooka.
Question:
Is there a way to space this? I have also tried to space according to length of variables, but this also does not really do any good and I'm not really keen on doing math for this...
Thanks for help!
The only option may be to post process the content. Build up the pretty content in a cfsavecontent, then run through cleanup function.
<cfprocessingdirective suppresswhitespace="No">
<cfsavecontent variable="message">
#tx_automailer_default_hello#
#tx_automailer_register_info#
#tx_automailer_register_iln#: #Local.User.iln#
#tx_firma#: #Local.User.firma#
#tx_ansprechpartner#: #Local.User.ansprechpartner#
#tx_adresse#: #Local.User.adresse#
#tx_plz#: #Local.User.plz#
#tx_ort#: #Local.User.ort#
</cfsavecontent>
<cfmail
TO="#Local.User.email#"
FROM="..."
SERVER="..."
USERNAME="..."
PASSWORD="..."
WRAPTEXT="80"
SUBJECT="#tx_automailer_register_subject# - #Local.User.Comp#"
>#cleanupTextMessage(message)#</cfmail>
<cffunction name="cleanupTextMessage" output="false">
<cfargument name="content" />
<!--- remove whitespace at beginning of each line --->
<cfset arguments.content = reReplace(arguments.content, "^\s+", "", "all") />
<!--- replace any multiple whitespace characters with one space --->
<cfset arguments.content = reReplace(arguments.content, "\s+", " ", "all") />
<cfreturn arguments.content />
</cffunction>
You might actually be able to nest the cfsavecontent inside cfmail, or create a custom tag that does savecontent and function actions.
Note: I was answering under the assumption the question was "how to make code look good without affecting the resulting text message". If you were trying to do something different with the resulting text output let me know.
You can use HTML To do it by adding the TYPE="html" to your cfmail attributes. Then put in a "pre" tag if you want that sysprint type look. as in
<pre>
#tx_automailer_default_hello#
#tx_automailer_register_info#
....
</pre>
Or you could add a table as in:
<table
<tr>
<td>#tx_automailer_default_hello#</td>
</tr>
<tr><td>
#tx_automailer_register_info#
</td>
If you want to stick with plain text you need to make sure you have tabs/spaces counted correctly and that none of your lines is longer than 80 chars (or they will wrap..without a beat too).
If you're set on plaintext email and are confident that the recipient will be using a fixed-width font, you can use lJustify() to align your text and pad with spaces.
Left justifies characters in a string of a specified length.
#lJustify(tx_automailer_register_iln & ":",32)# #lJustify(Local.User.iln,25)#
#lJustify(tx_firma & ":",32)# #lJustify(Local.User.firma,25)#
#lJustify(tx_ansprechpartner & ":",32)# #lJustify(Local.User.ansprechpartner,25)#
#lJustify(tx_adresse & ":",32)# #lJustify(Local.User.adresse,25)#
#lJustify(tx_plz & ":",32)# #lJustify(Local.User.plz,25)#
#lJustify(tx_ort & ":",32)# #lJustify(Local.User.ort,25)#
By default, ColdFusion passes simple types (like numeric, string, and GUID) by value to functions. I'd like to pass a simple type by reference.
I'm currently wrapping a simple value in a struct (they get passed by reference). This solves my problem but it is very ugly:
<!--- TheFunctionName---->
<cffunction name="TheFunctionName">
<cfargument name="OutVariable" type="struct">
<cfset OutVariable.ID = 5>
</cffunction>
<cfset OutVariable=StructNew()>
<cfset TheFunctionName(OutVariable)>
<!--- I want this to output 5--->
<cfoutput>#OutVariable.ID#</cfoutput>
I'd rather something like this:
<!--- TheFunctionName---->
<cffunction name="TheFunctionName">
<cfargument name="OutVariable" passbyref="true">
<cfset OutVariable = 5>
</cffunction>
<cfset TheFunctionName(OutVariable)>
<!--- I want this to output 5--->
<cfoutput>#OutVariable#</cfoutput>
AFAIK, there's no way to pass simple values by reference in ColdFusion. The only workaround I can think of is the one you're already using.
Instead, I would suggest trying to restructure your program to work with the grain of the language. In cases where there's only one simple value to "modify", you could just make your function return the new value, and call it like:
<cfset SomeVar = TheFunctionName(SomeVar)>
In cases where you're modifying multiple values, take a step back and think about whether it's possible to bundle those multiple values up into a CFC with your mutator functions becoming methods of the CFC. This could be clearer and more maintainable solution anyway.
You can arrange for the variables used outside and inside the function to be in a scope that exists in both code areas. For example, if you put a variable in the "session" or the "request" scope you will be able to access it from within the function. The changes made will persist.
Note that when you are doing this you aren't actually "passing" the variables to the function. The function just assumes the variable exists or creates it, depending on how you code it.
<cffunction name="TheFunctionName">
<cfset Request.StrVar = "inside function<br />" />
</cffunction>
<cfscript>
Request.StrVar = "outside function<br />";
WriteOutput(Request.StrVar);
TheFunctionName();
WriteOutput(Request.StrVar);
</cfscript>
About ColdFusion Scopes
If there is any doubt about the calling page declaring the variable in advance when it is required you'll have to do some legwork with the <cfparam> tag or IsDefined() function.
If you:
declare the function inside of a CFC
invoke the function using <cfinvoke>
You would be able to specify the <cfinvoke> parameter "returnvariable", and then output that variable however you like.
<cfinvoke component="this" method="TheFunctionName" returnvariable="blah">
<cfinvokeargument name="data" value="whatever" type="string">
<cfreturn data>
</cfinvoke>
<cfdump var="#blah#">
If you are writing everything in cfscript, then I would go with what SurroundedByFish said.