How to declare dynamic form field defaults inside a Coldfusion CFC? - forms

I'm looking for a way to declare form default values dynamically in a CFC, I'm calling via AJAX. The current CFC sends orders, which I need to break down into sub-orders.
I had been using this:
<!--- static defaults --->
<cffunction name="Defaults" access="public" returntype="struct" output="false"
hint="Assign default values to instance">
<cfscript>
var formDefaults = {
versenden=""
, speichern=""
...
}
</cfscript>
<cfreturn formDefaults />
</cffunction>
<cffunction name="Commit" access="public" returntype="struct" output="false" hint="database handler">
<!--- add dynamic form fields --->
<cfscript>
var LOCAL = {};
variables.defs = THIS.Defaults();
</cfscript>
<cfloop collection="#VARIABLES.Instance.FormData#" item="formField">
<cfscript>
if ( LEFT(formField, 5) EQ "MENGE"
OR LEFT(formField, 3) EQ "EAN"
OR LEFT(formField, 12) EQ "BESTELL_TEXT"
OR LEFT(formField, 10) EQ "BESTELLTYP"
...
) {
variables.defs[formField]="";
}
</cfscript>
</cfloop>
<cfscript>
structAppend(variables.defs, VARIABLES.Instance.FormData);
LOCAL.Basket = variables.defs;
</cfscript>
...
So I first declare static form fields (single instance only) and then try to dynamically append dynamic form fields to my array, which might be transferred multiple times (MENGE38, MENGE39, MENGE40 etc)
While this works ok, I need to add another counting element to my form-names, so I would have to change MENGE to something like counter.MENGE or MENGE.counter which will then send form values like this:
MENGE.1.38
MENGE.1.40
MENGE.1.41
MENGE.2.37
With the counter denoting the sub-order, this field is used for.
Problem is, this breaks my dynamic form field declaration and I don't understand why. I'm getting the following errors:
Diagnose: Element MENGE.1 is undefined in a CFML structure referenced as part of an expression.
Question:
Can anyone give me a hint on what the problem might be? Do I have to param the form fields on the HTML page as well (shouldn't have to)?
Thanks!
EDIT:
Problem was in my validate function, I also need to declare the modifications I did above. The new function looks like this:
<cffunction name="Validate" access="public" returntype="array" output="false" hint="validate form inputs and return an array of faulty field names.">
<cfscript>
var LOCAL = {};
var double = structNew();
double.form = VARIABLES.Instance.FormData;
double.criteria = VARIABLES.Instance.Validation;
</cfscript>
<!--- add dynamic form fields for validation... I FORGOT TO UPDATE THIS--->
<cfloop collection="#VARIABLES.Instance.FormData#" item="formField">
<cfscript>
if ( LEFT(formField, 5) EQ "MENGE"
OR LEFT(formField, 10) EQ 'BESTELLTYP'
OR LEFT(formField, 3) EQ "EAN"
OR LEFT(formField, 12) EQ "BESTELL_TEXT"
...
) {
VARIABLES.Instance.Validation[formField]="pass";
}
</cfscript>
</cfloop>
<!--- Get error names and type --->
<cfinvoke component="form_validate" method="validate_fields" double="#double#" returnvariable="validation_errors"></cfinvoke>
<cfset LOCAL.ErrorMessages = validation_errors />
<cfreturn LOCAL.ErrorMessages />
Because I did not add the new updated the if-clause in this function, I was getting the error.

To build on Dan Bracuk's answer, use underscores (though you'd need to change the name of "BESTELL_TEXT"). Use this with a combination of listFirst, listGetAt, and listLast to determine field name structure, using underscore as delimiter. Note how I cleaned up your big IF a bit using list function. This code as written probably doesn't do what you need, but wanted to illustrate the concepts without having to understand your business need.
<cfscript>
var orders=structNew();
item=listFirst(formField,'_');
orderNames = "MENGE,EAN,BESTELLTEXT,BESTELLTYPE";
if (listFindNoCase(orderNames,item,'_')){
if (!structKeyExists(orders,item)){
// initialize item
orders[item]=structNew();
}
orderID="";
subOrderId="";
if (listLen(formField,'_') gt 1) {
orderID=listGetAt(formField,2,'_');
}
if (listLen(formField,'_') eq 2) {
orders[item][orderId]=formData[formField];
}
if (listLen(formField,'_') eq 3) {
subOrderId=listLast(formField,'_');
orders[item][orderId][subOrderId]=formData[formField];
}
}
</cfscript>

Related

Lucee form scope not recognized

The problem below is a puzzle, which I have not solved, but found a way to deal with. I finally tore apart xref2.cfm and put it back together one line at a time. As soon as I did that, the form scope began to work. Nothing changed in the code. So it remains a mystery.
I have a set of 4 procedures which are linked together like this:
person1.cfm: this originates with a call from another program and a url argument basetab=Person . I'm focusing here on the variable "fn".
<cfif IsDefined('URL.basetab')>
<cfset basetab = URL.basetab>
<cfset perloc = URL.perloc>
<cfset fn = ''>
<cfset ln = ''>
<cfset eof = "no">
however person1.cfm may also be accessed with a cfinclude further down the line, in which case I rely on the form scope to pass fn back to person1.cfm
<cfelseif IsDefined('form.fn')>
<cfset fn = form.fn>
<cfset ln = form.ln>
<cfset basetab = form.basetab>
<cfset perloc = form.perloc>
<cfset eof = form.eof>
</cfif>
however, if neither the URL scope nor the form scope is defined here, the variable fn should have been set in a program which is including person1.cfm, so it should be well defined at this point.
... stuff ...
<form action = "person2.cfm" method = "post" ... other stuff>
<input type = "text" name = "fn" value = "#fn#">
.... more stuff....
... submit ...
</form>
person2.cfm: this is the next step in the process
... stuff ...
<cfset fn = form.fn>
<cfinclude template = "person1.cfm">
<form action = "xref1.cfm" method = "post" ... stuff >
<cfoutput>
<input type = "hidden" name = "fn" value = "#fn#">
</cfoutput>
.... stuff ....
.....submit ....
</form>
xref1.cfm this is the next step
... stuff ...
<cfset fn = form.fn>
<cfinclude template = "person2.cfm">
<form action = "xref2.cfm" method = "post" ... stuff >
<cfoutput>
<input type = "hidden" name = "fn" value = "#fn#">
</cfoutput>
.... stuff ....
.....submit ....
</form>
xref2.cfm the final step, where something is done with the accumulated entries
.... stuff ...
<cfset fn = form.fn>
<cfinclude template = "xref1.cfm">
... no form ... other stuff ...
The problem is that in xref2.cfm the variable fn is not being picked up in the form scope. Checking things out, I find that form.fn is not defined at all in xref2.cfm. When things cascade back to Person1, fn is not defined and throws an error in the input tag.
form.fn is defined in person2.cfm and in xref1.cfm. But when we get to xref2.cfm, it disappears. I do not understand why. Can someone explain to me why that form scope is not there in xref2.cfm?
I think it was a typo or something.

Counter withing a form output "#Form.Item(u)#"

I have a form that outputs different items in order the value of the items is coming out properly from the form (Value1,Value2,Value3) however when I try to do the same sort of counting method in my cfsets it does not work.
<cfloop index="u" from="1" to="#arraylen( session.cart )#">
<cfset updateRow = session.cart[u]/>
<cfset session.cart[#u#].Ohm = "#Form.ohmicValue1#">
Essentially what I need the "1" to be is a variable (#u#) but how do I got about landing that within the #Form.OhmicValue[#u#]# I guess is the final question.
Thanks for the help.
Tom
The following should work
<cfloop index="u" from="1" to="#arraylen( session.cart )#">
<cfset updateRow = session.cart[u]/>
<cfset session.cart[u].Ohm = form['ohmicValue' & u]>
</cfloop>

How to serialize html form in dart as a string for submission

In jQuery, there is a function to serialize a form element so for example I can submit it as an ajax request.
Let's say we have a form such as this:
<form id="form">
<select name="single">
<option>Single</option>
<option selected="selected">Single2</option>
</select>
<input type="checkbox" name="check" value="check1" id="ch1">
<input name="otherName" value="textValue" type="text">
</form>
If I do this with the help of jquery
var str = $( "form" ).serialize();
console.log(str);
the result would be
single=Single2&check=check1&otherName=textValue
Is there such functionality in dart's FormElement or I have to code it myself? Thanks.
I came up with my own simple solution that might not work in all cases (but for me it is workikng). The procedure is this:
First we need to extract all input or select element names and values from the form into Dart's Map, so the element name will be the key and value the value (e.g. {'single': 'Single2'}).
Then we will loop through this Map and manually create the resulting string.
The code might look something like this:
FormElement form = querySelector('#my-form'); // To select the form
Map data = {};
// Form elements to extract {name: value} from
final formElementSelectors = "select, input";
form.querySelectorAll(formElementSelectors).forEach((SelectElement el) {
data[el.name] = el.value;
});
var parameters = "";
for (var key in data.keys) {
if (parameters.isNotEmpty) {
parameters += "&";
}
parameters += '$key=${data[key]}';
}
Parameters should now contain all the {name: value} pairs from the specified form.
I haven't seen anything like that yet.
In this example Seth Ladd uses Polymers template to assign the form field values to a class which get's serialized.

MSXML2.ServerHTTP in Coldfusion 8 fails to post data

In trying to work around the buggy <cfhttp> SSL implementation, I switched to using COM and MSXML2.ServerHTTP directly. Unfortunately the POST data fails to arrive at the destination page. GET works, but I'd rather not use it, since I have no idea if the request urls are logged and I don't want to pass sensitive data if I can help it. (Am I worrying too much?)
My problem is similar to this one: http://objectmix.com/xml-soap/87408-sending-post-variables-using-msxml2-serverxmlhttp-3-0-a.html
Here's my code:
<cfset querystring = "?CustomerID=#CustomerID#&OrderDelRecipient=#OrderDelRecipient#&OrderCompany=#OrderCompany#&OrderTelephone=#OrderTelephone#&OrderNotes=#OrderNotes#&OrderDelStreet=#OrderDelStreet#&OrderDelCity=#OrderDelCity#&OrderDelState=#OrderDelState#&OrderDelZip=#OrderDelZip#">
<cfobject type="COM" action="Create" name="objSrvHTTP" class="MSXML2.ServerXMLHTTP.3.0">
<cfset urlstring = "http://www.pascaltechnologies.com/saveorder.cfm">
<cfset temp = objSrvHTTP.open("post", urlstring, false)>
<cfset temp = objSrvHTTP.setRequestHeader("Content-Type", "application/x-www-form-
urlencoded")>
<cfset temp = objSrvHTTP.send("#querystring#")>
<cfset FileContent = objSrvHTTP.responsetext>
<cfoutput>
#filecontent#
</cfoutput>
When I check for the Form vars on the called page (saveorder.cfm)
<cfif Not IsDefined("CustomerID") OR NOT IsNumeric(CustomerID)>
ERR 609 - INVALID PARAMETERS PASSED.
<cfabort>
</cfif>
they are empty, so the above code gets triggered.
Is this because I need to copy the response to the form var? e.g. <cfset FORM.CustomerID = objSrvHTTP.CustomerID> (You'd think it wouldn't be necessary since it isn't needed with GET.) Or am I simply not implementing something correctly?

Why does ColdFusion think that the value "7+" is a valid integer value, and how I can validate that it is not?

I have a form for users to input quantities. The form has client-side validation to ensure that the value is an integer and within a given range. The action page has server-side validation to ensure that the value is an integer and greater than zero.
However, one type of value gets through the validation and is causing my INSERT/UPDATE queries to throw exceptions. That value is an integer with a plus-sign - ie "7+" or "12+".
When such a value is entered, the ColdFusion-generated JavaScript validation throws a JavaScript error:
_CF_checkformAddToCart = function(_CF_this)
{
//reset on submit
_CF_error_exists = false;
_CF_error_messages = new Array();
_CF_error_fields = new Object();
_CF_FirstErrorField = null;
//form element itemQuantity 'INTEGER' validation checks
if (!_CF_checkinteger(_CF_this['itemQuantity'].value, false))
{
_CF_onError(_CF_this, "itemQuantity", _CF_this['itemQuantity'].value, "Error on itemQuantity, please enter an integer value for quantity that is not greater than 500");
_CF_error_exists = true;
}
//form element itemQuantity 'RANGE' validation checks
if (!_CF_checkrange(_CF_this['itemQuantity'].value, 0.0,500.0, false))
{
_CF_onError(_CF_this, "itemQuantity", _CF_this['itemQuantity'].value, "Error on itemQuantity, please enter an integer value for quantity that is not greater than 500");
_CF_error_exists = true;
}
}
Once I cancel out of the error popup, it goes to the action page, where I [try to] validate the value like so:
<cfif IsValid("integer", form.itemQuantity) AND form.itemQuantity GT 0>
<cfquery>
INSERT ....
However, if try this...
<cfset x = Int("7+") />
...ColdFusion throws an error.
Is it an integer or not ColdFusion???
How can get around this and validate my form input correctly?
isNumeric(form.itemQuantity) will return false for "7+", so to fully validate your input as an int, you can do this
<cfif isNumeric(form.itemQuantity) and IsValid("integer", form.itemQuantity) AND form.itemQuantity GT 0>
Due to the weird and wonderful nature of ColdFusion being typeless. It doesn't know what type of data you are working with and it tries to guess.
Its evaluating that 7+ is a valid. The validation built into ColdFusion makes a lot of assumptions and guesses.
My advise would be to not use it and to write your own validation routines that can be enhanced to do whatever you require.
For example
A user enters
2,075
Is this valid or invalid. Well if you have your own validation you can decide, you can say sure this is an integer and remote the , or you can say no they can't do that.
It's a small investment upfront that will pay off in the long run.
Turns out I can use LSParseNumber() to convert it to valid integer. So, now I'm testing for a valid integer, then resetting it using LSParseNumber() before attempting any database inserts:
<cfset addItemQty = 0 />
<cfif IsValid("integer", Trim(form.itemQuantity))>
<cfset addItemQty = LSParseNumber(Trim(form.itemQuantity)) />
</cfif>
I guess I'll have to re-engineer the front-end client-side validation to properly validate.