Coldfusion: Simple cfgridupdate validation - coldfusion

I trying to use a very simple CFGRID (with CFGRIDUPDATE) but not allow NULLS.
<cfset strWarning= "">
<cfif IsDefined("FORM.gridEntered")>
<cfif FORM.myName EQ '' OR FORM.myURL EQ ''>
<cfset strWarning= "Error: Form fields cannot be blank">
<cfelse>
<cfgridupdate grid="gridSomething"
dataSource="qryTSQL" tableName="tblName" keyOnly="Yes">
</cfif>
</cfif>
<cfoutput>#strWarning#</cfoutput><br />
<cfform>
<cfgrid name="gridSomething" query="qryTSQL"
selectMode="Edit" format="HTML">
<cfgridcolumn name = "myID" display="No">
<cfgridcolumn name = "myName">
<cfgridcolumn name = "myURL">
</cfgrid>
<cfinput type="submit" name="gridEntered">
The error I am getting is - Error Diagnostics: Complex object types cannot be converted to simple values. The expression has requested a variable or an intermediate expression result as a simple value. However, the result cannot be converted to a simple value. Simple values are strings, numbers, boolean values, and date/time values. Queries, arrays, and COM objects are examples of complex values.
The most likely cause of the error is that you tried to use a complex value as a simple one. For example, you tried to use a query variable in a cfif tag.
I think I am under thinking this desired solution.
Thanks.
EDIT: Updated Error Message
Error Diagnostics: Element MYURL is undefined in FROM.
Form scope - struct
FIELDNAMES GRIDENTERED,__CFGRID__CFFORM_1__GRIDSOMETHING
GRIDENTERED Submit Query
GRIDSOMETHING.MYID Form scope - array
1 1001
GRIDSOMETHING.MYURL Form scope - array
1 /test_d.cfm
GRIDSOMETHING.ORIGINAL.MYID Form scope - array
1 1001
GRIDSOMETHING.ORIGINAL.MYURL Form scope - array
1 /changed.cfm
GRIDSOMETHING.ROWSTATUS.ACTION Form scope - array
1 U
__CFGRID__CFFORM_1__GRIDSOMETHING __CFGRID__EDIT__=2 MYID Y MYURL Y 1 U 1001 1001 /test_d.cfm /changed.cfm

Grids work differently than standard FORM fields. Since grids contain multiples rows of data, CF creates arrays of the new/changed values. One for each grid column ie
FORM.gridname.columnName
To determine if any of the values for a specific column are empty, you need to loop through the array for that column and check each value:
<cfset updateCount = arrayLen(FORM.gridSomething.myName) />
<cfloop from="1" to="#updateCount#" index="x">
<cfset nameValue = trim( FORM.gridSomething.myName[x] ) />
<cfset urlValue = trim( FORM.gridSomething.myURL[x]) ) />
<!--- one or both of the values is empty --->
<cfif not len( nameValue ) OR not len( urlValue )>
... empty value found. do something here ....
</cfif>
</cfloop>

Add this function to you code and call it passing it the FORM scope as follows;
On page or CFC works the same!
Clears up errors with some versions of CF that choke on blank cols in a simple grid update
<cffunction name="cleanGridUpdate" >
<cfargument name="FORM" >
<cfloop collection="#FORM#" item="thisOne">
<cftry>
<cfset thisDeep = arrayLen(FORM[thisOne])>
<cfloop index="x" from="1" to="#thisDeep#">
<cfif len(FORM[thisOne][x])lt 1>
<cfset FORM[thisOne][x] = javaCast( "null", 0 )>
</cfif>
</cfloop>
<cfcatch>
<cfset cat=1>
</cfcatch>
</cftry>
</cfloop>
<cfreturn form>
</cffunction>
<cfif isDefined('URL.action') AND URL.action eq 'gridUpdate'>
<cfset cleanGridUpdate(FORM)>
<cfgridupdate grid="#URL.table#" table="#URL.table#" datasource="your_DS"
keyonly="yes" >
</cfif>
<cfform action="##?action=gridUpdate&table=cheesePuffs" method="post">
<cfgrid name='cheesePuffs' format='HTML'/>
<cfinput name="submit" type="submit" value>
</cfform>

Related

Coldfusion 10 cfloop errors

I am getting an error after upgrade from coldfusionOX to coldfusion 10.
Error Occurred While Processing Request Complex object types cannot be
converted to simple values.
The expression has requested a variable or an intermediate expression
result as a simple value. However, the result cannot be converted to a
simple value. Simple values are strings, numbers, boolean values, and
date/time values. Queries, arrays, and COM objects are examples of
complex values. The most likely cause of the error is that you tried
to use a complex value as a simple one. For example, you tried to use
a query variable in a cfif tag.
It occurs at line " cfloop index="local.thisRight" list="#rights#" ". It seems like ColdFusion does not like the "rights" here.
Anyone can give me some help? Thanks so much.
<cfif local.profile.rights.profile.self is not "">
<cfquery name="local.getAffiliations" datasource="#Request.readerDSN#">
SELECT tblPersonsToAffiliations.affiliationID, tblPersonsToAffiliations.rights, tblAffiliations.affiliationType, tblPersonsToAffiliations.relationshipType
FROM tblPersonsToAffiliations INNER JOIN tblAffiliations
ON tblPersonsToAffiliations.affiliationID = tblAffiliations.affiliationID
WHERE tblPersonsToAffiliations.personID IN (#local.profile.rights.profile.self#)
AND (tblPersonsToAffiliations.archived IS NULL
OR tblPersonsToAffiliations.archived = '')
</cfquery>
<cfif local.getAffiliations.recordCount is not 0>
<cfloop query="local.getAffiliations">
<cfif local.getAffiliations.relationshipType is "interested">
<cfset local.thisRelationshipType = "provisionalMember">
<cfif IsDefined("local.profile.rights.#affiliationType#.#local.thisRelationshipType#")>
<cfset local.profile.rights[affiliationType][local.thisRelationshipType] = ListAppend(local.profile.rights[affiliationType][local.thisRelationshipType], affiliationID)>
<cfelse>
<cfset local.profile.rights[affiliationType][thisRelationshipType] = affiliationID>
</cfif>
<cfelse>
<cfset local.thisRelationshipType = "fullMember">
<cfif IsDefined("local.profile.rights.#affiliationType#.#local.thisRelationshipType#")>
<cfset local.profile.rights[affiliationType][local.thisRelationshipType] = ListAppend(local.profile.rights[affiliationType][local.thisRelationshipType], affiliationID)>
<cfelse>
<cfset local.profile.rights[affiliationType][local.thisRelationshipType] = affiliationID>
</cfif>
<cfif isNull(rights)>
<cfelse>
<cfloop index="local.thisRight" list="#rights#" >
<cfif IsDefined("local.profile.rights.#affiliationType#.#local.thisRight#")>
<cfset local.profile.rights[affiliationType][local.thisRight] = ListAppend(local.profile.rights[affiliationType][local.thisRight], affiliationID)>
<cfelse>
<cfset local.profile.rights[affiliationType][local.thisRight] = affiliationID>
</cfif>
</cfloop>
</cfif>
</cfif>
</cfloop>
</cfif>
</cfif>
A bit earlier in your code you do this:
<cfif local.getAffiliations.relationshipType is "interested">
I think you need the same query name prefix in front of "rights" that is used when evaluating "relationshipType".
Try this:
#local.getAffiliations.rights#
HTH!
I am betting it is failing on this line:
<cfloop index="local.thisRight" list="rights" >
You are attempting to use the string "rights" as a list. My first reaction would be that you need to make that:
<cfloop index="local.thisRight" list="#rights#" >

Split the session variable into an array using recreating individual session variables

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>

Checking for key existence in structure

I have a variable named #cfData# which contains an array of structures. As it's clear from the image, for 1st structure array there are 6 keys and for 2nd, only two keys,viz date and open.
If I run a common loop, to go through each and every key, I will get an error at second array element. So the following only works when all the keys are present in the structure:
<cfset blockedtotal = 0 />
<cfset bouncetotal = 0 />
<cfset blocked = 0/>
<cfset datetotal = 0 />
<cfloop array = #cfData# index = "i">
<cfset blockedtotal += i.blocked />
<cfset bouncetotal += i.bounce />
</cfloop>
After reading online, I got an idea of using StructKeyExists where I think I can proceed in the following way:
<cfif structKeyExists(cfData,"bounce")>
<cfoutput>Bounce:#cfData.bounce#"/></cfoutput>
<cfelse>
<cfoutput> Bounce : [none]<br/></cfoutput>
</cfif>
But I am wondering, where exactly should I insert the above code inside the cfloop? Please advise if my approach is wrong.
Update:
Thanks guys. I got it running by using the following code based on the answers and it's running fine:
<cfloop array="#cfData#" index="i">
<cfif structKeyExists(i, "date")>
<cfset counter++>
<cfoutput>#counter#</cfoutput> Date is: <cfoutput> #i.date#</cfoutput> <br/>
</cfif>
</cfloop>
you don't need a "common loop". You can loop through each struct with
<cfloop array="#cfData#" index="i">
<cfloop collection="#i#" item="key">
struct with key '#key#' has data: #i[key]#
</cfloop>
</cfloop>
Of, if you need to decide if the struct has certain key, do something:
<cfloop array="#cfData#" index="i">
<cfif structKeyExists(i, "someKey")>
<cfset counter++>
</cfif>
</cfloop>

ColdFusion 9 - Error: Invalid list index 0

I thought I had this one licked...
But I can't seem to get it tracked down...
Funny thing is - the list delete function works... And performs the desired task...
But I still get this error...
Invalid list index 0. In function ListDeleteAt(list, index [, delimiters]), the value of index, 0, is not a valid as the first argument (this list has 12 elements). Valid indexes are in the range 1 through the number of elements in the list. The error occurred on line 735.
Code below
<cfset pwlist = "#add.pwlist#">
<cfset curlist = "#add.pwlist#">
<cfset ud = "#session.demshinuser_id#">
<cfoutput>
#curlist#
<br>
<br>
#pwlist#
<br><br>
#ud#<br>
<cfset newlist = ListDeleteAt(curlist, ListFind(pwlist,ud,","), ",")> <-- Error Here
#newlist#
</cfoutput>
<cfquery name=Update DATASOURCE="#ds#">
update shindates
set
pwlist = '#newlist#'
where shinid = '#shinid#'
</cfquery>
<cfif src is "cpwupc">
<cflocation url="upc.cfm" addToken="no">
</cfif>
<cfif src is "hcpw">
<cflocation url="list.cfm?typeid=#add.typeid#" addToken="no">
</cfif>
Found on Goog - trying this too to check if it a bad list thing... and ud isn't in list in first place...
<cfset pos = listfind(pwlist,ud)>
<cfif pos>
do list delete
<cfelse>
error. element not found. do something else
</cfif>

I need to output FORM variables to a report, but only if there is a value

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.