Can I use another cfswitch statement within the same cfswitch case?
<CFSWITCH EXPRESSION="nameOfPg">
<CFCASE VALUE="ClassMenu" >
<!---do something--->
</CFCASE>
<CFCASE VALUE="ReportsMenu">
<CFSWITCH EXPRESSION="#nameOfPg#">
<CFCASE VALUE="StudentMenu">
<!---do something--->
</CFCASE>
<CFCASE VALUE="DetailsMenu">
<!---do something--->
</CFCASE>
</CFSWITCH>
</CFCASE>
<CFDEFAULTCASE>
<!---do something--->
</CFDEFAULTCASE>
</CFSWITCH>
Yes, you can. The EXPRESSION attribute can be dynamic (e.g. a variable such as nameOfPg), but pay attention to the hashes #.
nameOfPg (your top CFSWITCH) evaluates the string, not the actual value of the variable nameOfPg. To resolve a variable in an attribute, you need to use hashes # like you did in your inner CFSWITCH: #nameOfPg#.
Fix your code:
<CFSWITCH EXPRESSION="#nameOfPg#">
<CFCASE VALUE="ClassMenu" >
<!---do something--->
and you should be good.
On a side note: CFCASE on the other hand does not allow the use of dynamic values (due to to the way a switch works in Java/ColdFusion). You always have to use a static value here like you do right now. Just keep that in mind.
Related
The typical way government offices make you record mileage is to enter one number per input box like the example below:
So what I am trying to do is create one input box that can split the session variable into an array. Then when that session variable is split into an array I would like it to set each value in the array to its own session variable.
Mileage Input:
123456 -<cfoutput>#session.checkout.vehicle.mileage#</cfoutput>
array [1,2,3,4,5,6]
6 -<cfoutput>#session.checkout.vehicle.mileage1#</cfoutput>
5 -<cfoutput>#session.checkout.vehicle.mileage2#</cfoutput>
4 -<cfoutput>#session.checkout.vehicle.mileage3#</cfoutput>
3 -<cfoutput>#session.checkout.vehicle.mileage4#</cfoutput>
2 -<cfoutput>#session.checkout.vehicle.mileage5#</cfoutput>
1 -<cfoutput>#session.checkout.vehicle.mileage6#</cfoutput>
So then I will be able to prefill in an already created form that has the boxes split for only one per box.
Where I am super confused and trying to comprehend is that there will not always be 6 variables. Let's say the mileage is 2344. I am assuming it will need to know to start backwards, counting from the right to the left. That's why I started 6 at #session.checkout.vehicle.mileage1#
Hopefully I have not super confused anyone with what I am trying to do. And any help would be greatly appreciated!!
<cfparam name="form.mileage" default="#session.checkout.vehicle.mileage#">
...
<label for="mileage">Mileage:</label>
<input type="text" name="mileage"
id="mileage"
value="<cfoutput>#form.mileage#</cfoutput>">
Edit:
The issue I am having with this is let's say the mileage is 9000 all 0's will not show. (which is great for the first two zero's in (009000) but after the 9 those 0's would still need to show.) Do you any ideas for that issue? Or should this be a new question?
<cfset Mileage = "9000" />
<cfif mileage is not "Exempt">
<cfset Mileage = NumberFormat(trim(Mileage),"000000") />
<cfset MilArray = ReMatch("\d",Mileage) />
<cfelse>
<cfset MilArray = ["E","x","e","m","p","t"]>
</cfif>
<cfdump var="#MilArray#">
<cfif MilArray[1] is not "0">
<!---Section6 First box Odometer Reading--->
<cfpdfformparam name="E" value="#MilArray[1]#">
<cfelse>
<cfpdfformparam name="E" value="">
</cfif>
If I'm understanding, you want to divide the string into six easy to work with variables, or whatever the length of the variable is.
<cfset Mileage = "123456" />
<cfset MilArray = ReMatch("\d",Mileage) />
<cfdump var="#MilArray#" />
You can actually stick a Reverse() in there to reverse the string, this may be handy because you can have [1] at the ones place, [2] at tens, [3] at hundreds.
<cfset Mileage = "123456" />
<cfset MileageR = Reverse(Mileage) />
<cfset MilArray = ReMatch("\d",MileageR) />
<cfdump var="#MilArray#" />
\d by itself in regular expressions just means "one digit". It's the same as [0-9].
As the CFDUMP will show, ReMatch will split your mileage into an easy to work with array. If you use the reverse as above, you can say "The last digit of your mileage is #MilArray[1]#.", as an example.
Edit:
you know the \d ? is there a way to have it be either \d or only the word Exempt? is it possible to create both of those?
There are a few ways.
You can say
<cfif mileage is not "Exempt">
...
<cfelse>
<cfset MilArray = ["Exempt"]>
</cfif>
which creates a one dimensional array populated with "Exempt" as the only element, which might be useful later in your code so you know MilArray is always an array, or you can simply always work with the <cfif mileage is not "Exempt">.
A regex to accomplish the same thing is possible but it achieves the same as the above cfif, and you'd have to write exempt backwards if you're using reverse, like this
<cfset MilArray = ReMatchNoCase("\d|^Exempt$|^tpmexE$)",trim(Mileage)) />
<cfif MilArray[1] is "tpmexE"><cfset milArray = ["Exempt"] /></cfif>
Edit #2:
<cfif isDefined("session") and structKeyExists(session, 'checkout') and structKeyExists(session.checkout, 'info') and structKeyExists(session.checkout.info, 'oreading')>
<cfif isDefined("#MilArray[6]#") eq "">
<cfpdfformparam name="E" value="">
<!---Section6 First box Odometer Reading--->
<cfelse>
<cfpdfformparam name="E" value="#MilArray[6]#">
</cfif>
</cfif>
This is a task for ArrayIsDefined() (link)
<cfif isDefined("session") and structKeyExists(session, 'checkout') and structKeyExists(session.checkout, 'info') and structKeyExists(session.checkout.info, 'oreading')>
<cfset MilArray = ReMatch("\d",session.checkout.info.oreading) />
<cfif not ArrayIsDefined(MilArray,6)>
<cfpdfformparam name="E" value="">
<!---Section6 First box Odometer Reading--->
<cfelse>
<cfpdfformparam name="E" value="#MilArray[6]#">
</cfif>
.... I assume that it continues on down from here... <cfif not ArrayIsDefined(MilArray,5)>........</cfif>
</cfif>
Finally, while there's contention here on whether to use StructKeyExists() over IsDefined(), there's a narrow field where isDefined() fails.
(Don't put structures in the top level and in the variables scope. Cold Fusion gets confused--IE, don't create an object called "variables.form" or "variables.url"). Beyond that, It's mostly just semantics.
Anyway. once you have the above code working (because it's your code and your familiar with it), you might find it useful to switch to the easier to read IsDefined() version, because isDefined can check several levels deep in one condition.
<cfif isDefined("session.checkout.info.oreading')>
<cfset MilArray = ReMatch("\d",session.checkout.info.oreading) />
<cfif not ArrayIsDefined(MilArray,6)>
<cfpdfformparam name="E" value="">
<!---Section6 First box Odometer Reading--->
<cfelse>
<cfpdfformparam name="E" value="#MilArray[6]#">
</cfif>
</cfif>
Edit 3:
Leigh points out
Why so complicated? Can't you just left pad the value with spaces or zeroes? Then change the regex to check for either a digit or space? Then the array will always have six elements
This can be achieved like this:
<cfset Mileage = "exempt" />
<cfif mileage is not "Exempt">
<cfset Mileage = NumberFormat(trim(Mileage),"000000") />
<cfset MilArray = ReMatch("\d",Mileage) />
<cfelse>
<cfset MilArray = ["E","x","e","m","p","t"]>
</cfif>
<cfdump var="#MilArray#">
Which would conveniently drop Exempt into place (handy that it's 6 characters).
You need to do some prechecking before you start generating the pdf to make sure that mileage variable is Exempt or or numeric.
<cfif len((trim(mileage)) gt 6 or not ((isNumeric(trim(mileage))
or mileage is "exempt")>
<!--- The 6 above is a len-check, you may need to update that number to
something else later, but you'll have to put the same number of 0s
in the NumberFormat function.
If you change that number, and the 0s, you'll need to pad the
"Exempt array"... ie ["E","x","e","m","p","t"," "] --->
....raise a flag...
</cfif>
Here is a simpler way.
originalNumber = "123";
sixDigitNumber = right(("000000" & originalNumber), 6);
<cfoutput>
<cfloop from="1" to = "6" index="position">
do something useful with #Mid(sixDigitNumber, position, 1)#
</cfloop>
<cfoutput>
I am trying to check to see if data exist in my form If data does not exist I want to assign it to O. How can I do this.
<cfif not isDefined("FORM.Age")>
cfset FORM.Age = "0"
<cfif>
Generally the best practice is considered to be to avoid isDefined. This is because isDefined will search all scopes until it finds a matching variable. So it's more efficient to use structKeyExists, eg:
<cfif NOT structKeyExists(form, "age")>
<cfset form.age = 0>
</cfif>
Also, another way to achieve this is to use cfparam, and specify 0 as the default:
<cfparam name="form.age" default="0">
You're almost there:
<cfif not isDefined("FORM.Age")>
<cfset Form.Age = 0>
</cfif>
Technically what you have is fine once you enclose the cfset in tags < and >. Assuming that omission is just a typo, could it be you are trying to use it with a text field?
Text fields always exist on submission. The value may be an empty string, but the field itself still exists, so IsDefined will always return true. If that is the case, you need to examine the field length or value instead. Then do something if it is empty according to your criteria. For example:
<!--- value is an empty string --->
<cfif NOT len(FORM.age)>
do something
</cfif>
... OR
<!--- value is an empty string or white space only --->
<cfif NOT len(trim(FORM.age))>
do something
</cfif>
... OR
<!--- convert non-numeric values to zero (0) --->
<cfset FORM.Age = val(FORM.Age)>
There are actually two things you want to to ensure. First, make sure this page was arrived at by submitting the proper form. Next, ensure you have a numeric value for the form.age variable. Here is an example of how you might want to code this:
<cfif StructKeyExists(form, "age") and cgi.http_referrer is what it should be>
<cfif IsNumeric(form.age) and form.age gt 0>
<cfset AgeSubmitted = int(form.age)>
<cfelse>
<cfset AgeSubmitted = 0>
</cfif>
...more code to process form
<cfelse>
...code for when page was not arrived at properly
</cfif>
There is a search query, and a function I am using to generate query elements
<cffunction name="GetSearchQuery" output="true" returntype="string" access="public">
<cfargument name="arrayName" type="array" required="yes">
<cfargument name="columnName" type="string" required="yes">
<cfargument name="searchtype" type="string" required="no" default="wildcard">
<cfset var o = "">
<cfset var i = "">
<cfset var search_item = "">
<cfset search_item = "(">
<cfloop from="1" to="#ArrayLen(Arguments.arrayName)#" index="o">
<cfif Arguments.arrayName[o][1] EQ #Arguments.columnName#>
<cfloop from="2" to="#ArrayLen(Arguments.arrayName[o])#" index="i">
<cfset search_item = search_item & #Arguments.columnName#>
<cfswitch expression="#Arguments.searchtype#">
<cfcase value="wildcard">
<cfset search_item = search_item & ' LIKE
<cfqueryparam value="%' & #Arguments.arrayName[o][i]# & '%"> AND '>
</cfcase>
<cfcase value="startswith">
<cfset search_item = search_item & ' LIKE
<cfqueryparam value="' & #Arguments.arrayName[o][i]# & '%"> AND '>
</cfcase>
<cfcase value="endswith">
<cfset search_item = search_item & ' LIKE
<cfqueryparam value="%' & #Arguments.arrayName[o][i]# & '"> AND '>
</cfcase>
<cfcase value="exactmatch">
<cfset search_item = search_item & ' =
<cfqueryparam value="' & #Arguments.arrayName[o][i]# & '"> AND '>
</cfcase>
</cfswitch>
</cfloop>
</cfif>
</cfloop>
<cfif Len(search_item) GT 4>
<cfset search_item = Left(search_item, Len(search_item)-4) & ") ">
</cfif>
<cfreturn search_item>
</cffunction>
And then call it like this in the query
SELECT * FROM #request.tablename#
WHERE #utilObj.GetSearchQuery(arrsearch, "photonumber", true)# OR
#utilObj.GetSearchQuery(arrsearch, "takenby", true)# OR
#utilObj.GetSearchQuery(arrsearch, "category", true)# OR
#utilObj.GetSearchQuery(arrsearch, "area", true)# OR
#utilObj.GetSearchQuery(arrsearch, "description", true)#
But it causes an error in the query
But without cfqueryparam in the function this woks fine.
eg. <cfset search_item = search_item & ' LIKE "%' & #Arguments.arrayName[o][i]# & '%" AND '>
Is there anyway we can add cfqueryparam dynamically to a query?
You can't create a string containing CFML and output it and expect that to somehow mean it'll be actually executed.
For one thing, that's a bit daft when you stop and think about it, innit? (sorry, I don't mean that in a mean-spirited way). And don't feel bad: I reckon we've all done this at some stage.
Secondly: CFML is compiled before it's executed. So the process is (for all intents and purposes):
File containing code is requested
Code from file is passed to the CF
compiler
CF compiler spits out java byte code
JVM executes java byte code
So your code to generate the string with the CFML code is not executed until (4), but it is needed back at (2). Unless you can time travel, that ain't gonna work.
I discuss this in my blog: "The ColdFusion request/response process"
There's a coupla things you can do:
don't write dynamic generic SQL like this. All of us do it when we first start, but quickly come to realise dynamic/generic SQL is never a good solution to whatever issue is at hand.
Use one of the already-existing DB abstraction tiers out there. CF 9+ comes with Hibernate support baked in.
If you use Query.cfc instead of <cfquery>, you can put placeholders in for the parameters, and pass the parameter data into the query separately.
Write your dynamic code to disk, then include it. This'll subvert the compile-time/run-time thing. It will be slow, as your included file will need to be compiled before it will run. It's ugly.
That list is in my order of preference for dealing with this issue.
This is in response to "Is there anyway we can add cfqueryparam dynamically to a query?". There is, but you have to do it inside the cfquery block. Something like this is ok:
<cfquery>
select SomeFields
from SomeTables
where 1 = 1
<cfif something>
and somefield = <cfqueryparam value="#SomeVariable#">
</cfif>
</cfquery>
What you can't do, at least not on version 9 and lower, is to make query parameters part of a variable. In other words, this will not compile.
WhereClause = "where 1=1";
if (something)
WhereClause &= ' and somefield = <cfqueryparam value="#SomeVariable#">';
This is more or less what you were attempting.
I have a form with many values in it (around 25 fields). After the form is posted and inserted into the database, I have to take the form information and output it to a report. The report should only show fields with those that have values in it (so the report would only have 5 fields in it, if only 5 fields were filled in).
The easiest way would be to do something like this:
<cfif form.firstname neq "">
<li><First Name: #FORM.FIRSTNAME#</li>
</cfif>
<cfif form.lastname neq "">
<li><Last Name: #FORM.LASTNAME#</li>
</cfif>
Can anyone recommend a better way of doing this? I would like to keep it on the ColdFusion side, since the entire report is stripped of HTML to produce a plain text report as well.
You can loop through them like this
<cfloop list="#form.fieldNames#" index="i">
<li><cfoutput>#i# = #form[i]#</cfoutput></li>
</cfloop>
Not sure that is exactly what you want but it might get you on the right track
Based on your comment try this :
<cfloop list="#form.fieldNames#" index="i">
<li><cfoutput>
<cfswitch expression="#i#">
<cfcase value="firstName">
First Name
</cfcase>
<cfcase value="lastName">
Last Name
</cfcase>
<cfdefaultcase>
#i#
</cfdefaultcase>
</cfswitch>
: #form[i]#</cfoutput></li>
</cfloop>
Here's my attempt at this: Thanks to Lance for pointing me towards the right direction:
<ul>
<cfoutput>
<cfloop list="#form.fieldNames#" index="i">
<li>
<cfif len(trim(form[i])) neq 0>
<cfswitch expression="#i#">
<cfcase value="FIRST_NAME">First Name</cfcase>
<cfcase value="LAST_NAME">Last Name</cfcase>
<cfdefaultcase>#i#</cfdefaultcase>
</cfswitch>
: #FORM[i]#
</cfif>
</li>
</cfloop>
</cfoutput>
</ul>
Here's an alternative to using switch/case - put labels in a struct
<cfset FieldLabels =
{ 'first_name' : "First Name"
, 'last_name' : "Last Name"
, 'other stuff' : "Whatever you like"
}/>
<cfoutput>
<ul>
<cfloop index="CurField" list=#Form.FieldNames# >
<cfif len(trim( Form[CurField] )) >
<li>
#StructKeyExists( FieldLabels , lcase(CurField) )
? FieldLabels[ lcase(CurField) ]
: HtmlEditFormat( replace(CurField,'_',' ','all') )
#
: #HtmlEditFormat( Form[CurField] )#
</li>
</cfif>
</cfloop>
</ul>
</cfoutput>
Note that it does not do neq 0 on the len - this is entirely unnecessary.
The a ? b : c construct inside the first hashes is the ternary conditional operator - a compact way of doing if/else - supported in CF10 and Railo 3.3 onwards.
If a name doesn't have an explicit label, it replaces underscores with spaces, which is probably preferable if the report is for a non-technical audience.
If it's possible that you have code that adds (or removes) items in the form scope without modifying the FieldNames list, you can change the loop for this...
<cfloop item="CurField" collection=#Form# >
<cfif CurField EQ 'FieldNames'>
<cfcontinue />
</cfif>
...
Which looks at the actual keys in the Form scope - though the ordering of them is not guaranteed for this method.
How about you loop on the struct itself directly ?
<cfscript>
for(eachKey in FORM){
if(FORM[eachKey] neq ''){
writeOutput('<li>' &
FORM[eachKey] & ' = ' &
trim( FORM[eachKey] ) &
'</li>');
}
}
</cfscript>
You can have a space in the form variable name.
Is there a logical difference between the following two blocks? And is there one form more correct than the other? They would both reside in their own function--something I omitted here.
<cfset local.result = 1 />
<cfset local.i = 1 />
<cfloop from="1" to="5" index="i">
<cfset result = result * i />
</cfloop>
And
<cfset local.result = 1 />
<cfset local.i = 1 />
<cfloop from="1" to="5" index="i">
<cfset local.result = local.result * local.i />
</cfloop>
Yes. In your second example you are making the exact same result; however, you have improved readability by explicitly identifying the scope you intend to modify--which is a good thing.
ColdFusion, will first search the LOCAL scope, so, you have not saved ColdFusion much processing; however, the code is cleaner now. If result existed in the CLIENT or COOKIE scope you would have saved ColdFusion the work of having to first evaluate four or five other scopes.
I once used to use the 'var result = 0;' style of localizing variables to a function, but, I now explicitly identify all my scopes to help ensure I have correctly scoped all variables and make the code easier to understand for others.
To summarize, the code is exactly the same to the machine but is now easier to understand for the human.
One suggestion... change:
<cfset local.i = 1 />
<cfloop from="1" to="5" index="i">
to
<cfloop from="1" to="5" index="local.i">
one less line of code, even more clear what's going on.