Creating a structure as a button in ColdFusion - coldfusion

First off, I'm not really sure how to title this question so I apologize if it's vague. I am trying to create a shopping list using ColdFusion and I've ran into a bit of a snag. I want a delete button to appear next to the item that's been created. I have almost everything working, but I don't understand structures enough in ColdFusion to understand what I am doing wrong. Is it similar to a component in React.js? I ran into an issue saying that the variable "button" is not defined. I'm assuming this is because structkeyExists can't identify a single button. Why would this work for form and not for a button?
Here is my code:
<cfif structKeyExists(form, "submitButt")>
<cfquery datasource="ESC-ADD-TECH">
INSERT INTO Main(itemDesc) VALUES('#itemDesc#')
</cfquery>
</cfif>
<cfif structKeyExists(button, "delete_butt")>
<cfquery datasource="ESC-ADD-TECH">
INSERT INTO Main(itemDesc) VALUES('#itemDesc#')
</cfquery>
</cfif>
<cfquery datasource="ESC-ADD-TECH" name="items">
DELETE FROM Main
WHERE itemDesc = '#itemDesc#'
</cfquery>
<body>
<div id="myDIV" class="header">
<h2>My Shopping List</h2>
<form method="POST">
<input type="text" name="itemDesc" placeholder="Title...">
<input name="submitButt" type="submit" class="addBtn">
</form>
</div>
<cfoutput query="items">
<li>#items.itemDesc# <button class="delete" name="delete_butt">x</button></li>
</cfoutput>
</body>
Is there a way to do what I am trying to do here using a structure? Am I better off creating the button in javascript and try to create a structure as a boolean statement and just have javascript rewrite that value? Kinda just shooting in the dark here, but I would appreciate any and all help.
Thank you everyone!

So there isn't going to be a "button" structure from your form being submitted.
The first thing to remember is that a ColdFusion structure is just a collection of key/value pairs (similar to a JavaScript object), and unless the value is set, will be undefined.
In your case, the "form" struct exists because you are submitting your page back to itself with your input[type="submit"]. Which for a ColdFusion page, will create a form struct with keys for each named input within the submitted form, the values of which are pulled from those elements' value attributes.
If you are trying to use the form struct to handle deleting items, you may be better served using radio buttons/checkboxes to select which item(s) to delete, and set the action to take using the value attribute of your submit buttons.
Using your code as an example:
<cfparam name="form.action" type="string" default="none">
<cfswitch expression="#form.action#">
<cfcase value="insert">
<!---Your insert query goes here--->
</cfcase>
<cfcase value="delete">
<!---Your delete query goes here--->
</cfcase>
<cfdefaultcase></cfdefaultcase>
</cfswitch>
<!---Your select query--->
<body>
<form method="post" action="#">
<div id="myDIV" class="header">
<h2>My Shopping List</h2>
<input type="text" name="itemDesc" placeholder="Title...">
<button type="submit" name="action" value="insert">Submit</button>
</div>
<ul>
<cfoutput query="items">
<li>#items.itemDesc#
<input type="radio" name="delDesc" value="#items.itemDesc#"/>
</li>
</cfoutput>
</ul>
<button type="submit" name="action" value="delete">Delete</button>
</form>
</body>
In this case you will use form.itemDesc when inserting values, and form.delDesc when deleting items.

Related

select statement displays full list but not in a select statement

I am struggling to find out where I have gone wrong, and have spent hours trying to find out why this is happening, I think its just something I have missed but I cant spot it.
What happens is that the select statement instead of showing a dropdown which i can just select a team to delete displays all of the teams in a list with a delete button next to it, yet when I use the select statement on another page it works fine. if anyone can point out where I have gone wrong that would be appreciated
Cheers
<cfquery name="deleteteam" datasource="danny2">
SELECT *
FROM pool_teams
</cfquery>
<html>
<head>
<title>LCF Delete Team</title>
</head>
<body>
<cfif IsDefined('Form.delete_button')>
<cfoutput>
<form action="#CGI.SCRIPT_NAME#" method="post">
<input type="hidden" name="ID" value="#FORM.ID#"/>
do you really want to delete record?
<input type="submit" name="confirm_button" value="Yes">
<input type="submit" name="cancel_button" value="No">
</form>
</cfoutput>
<cfelseif IsDefined('FORM.confirm_button')>
<cfquery datasource="danny2">
DELETE FROM pool_teams
WHERE ID = '#FORM.ID#'
</cfquery>
The record has been deleted
<cfoutput> Return to list</cfoutput>
<cfelseif IsDefined('FORM.cancel_button')>
<cflocation url="#CGI.SCRIPT_NAME#" >
<cfelse>
<cfoutput query="deleteteam">
<form action="#CGI.SCRIPT_NAME#" method="post">
<select>
<option value="#ID#">#teamname#</option>
</select>
<input type="hidden" name="ID" value="#deleteteam.ID#">
<input type="submit" name="delete_button" value="delete"/>
</form>
</cfoutput>
</cfif>
</body>
</html>
<cfoutput query="deleteteam">
<form action="#CGI.SCRIPT_NAME#" method="post">
<select>
<option value="#ID#">#teamname#</option>
</select>
<input type="hidden" name="ID" value="#deleteteam.ID#">
<input type="submit" name="delete_button" value="delete"/>
</form>
</cfoutput>
This is where you are going wrong. If you think of outputting a query as performing a loop. each time you loop over the query you are making another form with one select option in it.
You should change your code to look something like this.
<form action="#CGI.SCRIPT_NAME#" method="post">
<select>
<cfoutput query="deleteteam">
<option value="#deleteteam.ID#">#deleteteam.teamname#</option>
</cfoutput>
</select>
<input type="hidden" name="ID" value="#deleteteam.ID#">
<input type="submit" name="delete_button" value="delete"/>
</form>
What My code is doing is just adding options for each query item not the complete from.
Hope that makes sense.

How can I keep form values without using session?

I have one form page with pagination. I want to keep the form values as the user goes to the previous or next page, using pagination. I know that it can be done using the session scope. However, here I don't want to use session scope. Does anyone have any ideas on how to do this without using session? Please let me know.
Here is my form page:
<cfoutput>
<form action="#buildUrl(action='survey.save_surveyresults',querystring='surveyId=#rc.surveyid#')#" method="post">
<input type="hidden" name="id" value="0">
<input type="hidden" name="surveyid" value="#rc.surveyId#">
<div class="container-fluid">
<div class="row">
<div class="control-group">
<label class="label-control" for="name">Name</label>
<div class="controls">
<input type="text" name="name" id="name" required="true" placeholder="enter your name" value="#rc.name#">
</div>
</div>
<div class="control-group">
<label class="label-control" for="email">Email</label>
<div class="controls">
<input type="text" name="email" id="email" required="true" placeholder="enter your Email" value="#rc.email#">
</div>
</div>
<cfloop query="rc.questions" startrow="#startrow#" maxrows="#perpage#">
<!--- because we have all questions and answers in query we can use switch instead calling template or view
for each question, so its simplify directory structures, questions directory is not necessary now --->
<h3>#CurrentRow#<cfif rc.questions.isrequired><strong>*</strong></cfif>. #rc.questions.question#<h3>
<cfswitch expression="#rc.questions.template#">
<fieldset>
<cfcase value="textbox">
<input type="text" class="input-xlarge" name="#template#_#questionid#" id="question_#questionid#">
</cfcase>
<cfcase value="multiplechoice">
<cfloop list="#answer#" delimiters="," index="i">
<div class="controls">
<label>
<input type="radio" name="#template#_#questionid#" id="question_#questionid#" value="#answerID#" >
<span class="lbl">#i#</span>
</label>
</div>
</cfloop>
</cfcase>
<cfcase value="multiplechoiceother">
<cfloop list="#answer#" delimiters="," index="i">
<div class="controls">
<label>
<input type="radio" name="#template#_#questionid#" id="question_#questionid#" value="#answerID#" >
<span class="lbl">#i#</span>
</label>
</div>
</cfloop>
<div class="control-group">
<label class="label-control" for="other">Other</label>
<div class="controls">
<input type="text" class="input-xlarge" name="#template#_#questionid#" id="question_#questionid#">
</div>
</div>
</cfcase>
</fieldset>
</cfswitch>
</cfloop>
<p></p><br />
<cfif startrow GT 1>
Previous
</cfif>
<cfif (startrow + perpage - 1) lt rc.questions.recordcount>
Next
<cfelse>
<button type="submit" name="submit" class="btn btn-success">Finish</button>
</cfif>
</div>
</div>
</div>
</form>
</cfoutput>
You could break the form up into different sections and have it all in one page. You can hide/show parts of the form using JavaScript based on which 'page' of the form they are one.
This makes going forward or backward in the form a snap since it is not submitted until they are done with the whole form and the values they entered will still be there.. and is pretty easy to handle with jQuery or other JavaScript libraries.
As Dan said - save submitted values in hidden fields.
One issue I see with your HTML is that Previous/Next pages are just links - not submit buttons. So make sure that when clicking those links users are submitting the form - not just going to a different url.
Here's a simple snippet of code that will embed all your form variables into hidden fields. You would place this code inside the form handler on the page you are submitting to. Note Lucas' answer as well. Your form may not be submitting correctly for reasons of badly formed..er...form.
<Cfloop collection="#form#" item="fItem">
<cfoutput>
<input type="hidden" name="#fItem#" value="#form[fItem]#"/>
</cfoutput>
</cfloop>
Again .. this would go _inside" of the form on the subsequent page. This is fairly common in multipart forms (shopping carts with multiple steps, profile entries etc).
Bear in mind that with the approaches above, you need to re-validate your form values on the server side every time you submit them (or at the very least before your final processing).
What you make up for in server memory, you may lose in terms of traffic and load times, depending on scale so I would advise that you proceed with caution. Increasing production traffic unnecessarily can result in financial impacts, and often server memory can be cheaper than extended increased traffic outlay; it comes down to your requirements and scale at the end of the day.
Shipping form variables around also increases your attack surface for malicious injection of form data, so while you may be concerned with session variables being altered on you (curious to hear more on this), you are already opening yourself up by shipping this data around as plain text. Do not rely on client-side validation for this (or any) data.

displaying questions twice

can anyone review this code tell me what's wrong in it? I don't understand why it is displaying questions two times.
here is the code to display questions based on its questiontype, I mean it will look into question folder for matching questiontype template and then display it with question.
this is the code to show survey's questions.
<cfoutput>
<cfset step = 0 />
<form class="form form-horizontal" action="#buildUrl(action='survey.savesurveyresults',querystring='surveyId=#rc.surveyid#')#" method="post">
<input type="hidden" name="id" value="0">
<input type="hidden" name="fksurveyid" value="#rc.surveyId#">
<input type="hidden" name="fkquestionid" value="#rc.questions.id#">
<fieldset>
<cfloop query="rc.questions">
<cfset step ++ />
<cfset answer = "" />
<cfmodule template="../question/#rc.questions.template#/display.cfm" step="#step#" question="#rc.questions.question#" template1="#rc.questions.template#" fkquestionid="#rc.questions.id#" answer="#answer#" required="#rc.questions.required#" result="result#step#"/>
</cfloop>
<div class="form-actions">
<button type="submit" name="submit" class="btn btn-success">Submit answers</button>
</div>
</fieldset>
</form>
</cfoutput>
this is my display.cfm to view question and its questiontype like truefalse or yes or no.
<cfparam name="attributes.yesno" default="false">
<cfoutput>
<p>#attributes.step#) #attributes.question# <cfif attributes.required EQ 1><strong>* </strong></cfif></p>
<div class="answers">
<cfif attributes.yesno>
<input type="radio" name="answer" id="answer" value="yes"<cfif attributes.answer is "yes">Checked</cfif>><label for="truefalse">Yes</label><br>
<input type="radio" name="answer" id="answer" value="no"<cfif attributes.answer is "No">Checked</cfif>><label for="truefalse">No</label>
<cfelse>
<input type="radio" name="answer" id="answer" value="true"<cfif attributes.answer is "true">Checked</cfif>><label for="truefalse">True</label><br>
<input type="radio" name="answer" id="answer" value="False"<cfif attributes.answer is "False">Checked</cfif>><label for="truefalse">False</label>
</cfif>
</div>
</cfoutput>
here is the query to list question's records.
<cfquery name="list">
SELECT
questions.id,
questions.question,
questions.rank,
questions.required,
questiontypes.name as questiontype,
questiontypes.template as template,
surveys.name as surveysname,
surveys.thankyoumsg as thankyoumsg
FROM questions
INNER JOIN questiontypes ON questions.fkquestiontypeid = questiontypes.id
INNER JOIN surveys ON questions.fksurveyid = surveys.id
WHERE questions.fksurveyid = <cfqueryparam cfsqltype="cf_sql_bigint" value="#arguments.surveyid#">
</cfquery>
This is something that has bitten me a couple of times. I've always been pretty big on closing tags. But this is a situation where it will hurt. And be hard to debug if you don't understand the behavior of cfmodule. As the post above mine states, if you close the cfmodule tag, it will execute twice. This is because it's treated the same as a custom tag. There may be situations where you want to process part of the tag when it's first run and the rest after it's complete. You can access the ExecutionMode in the thisTag scope of the cfmodule page. With no closing tag, it's simply run in the thisTag.ExecutionMode = Start mode. If you close it, it runs the tag again in the End mode. If you aren't doing anything with the ExecutionMode inside the cfmodule's code, the whole thing will simply run again. This behavior is part of the reason that cfmodule can be so powerful.
When using the <cfmodule> tag you need to remember that ColdFusion will call that tag twice if you include an ending </cfmodule> tag OR if you close the opening tag like so <cfmodule ... />.
As stated on the cfmodule documentation page:
If you specify an end tag to cfmodule, ColdFusion calls your custom tag as if it had both a start and an end tag. For more information, see Handling end tags in the Developing ColdFusion Applications.
Handling end tags in the Developing ColdFusion Applications
In order to avoid this functionality do not close your <cfmodule> tag.
I got it, i must have not to close the cfmodule tag like <cfmodule />.

How to display each element in a ColdFusion query

I have written this piece of CF code to get and display the data from database. (Actually to populate text fields.) The problem: I am not getting values from the query, but I am correctly getting the number of records.
How am I supposed to access the values returned by the query via a cfloop? Below is my work.
<cfquery name="data_query" datasource="#dsn#">
SELECT
id,
name
FROM learning
</cfquery>
<cfloop query=data_query">
<li>
<div class="list_div clearfix">
<input type="text" value="#URLDecode(name)#">
</div>
</li>
</cfloop>
</cfquery>
You have two options:
Wrap the vars output line with <cfoutput /> tags:
<cfoutput>#id#: <input type="text" value="#name#"></cfoutput>
Use <cfoutput query="data_query"> loop instead of <cfloop ...>
For the sake of cleaner code I would prefer the second option so your code would be:
<cfquery name="data_query" datasource="#dsn#">
SELECT
id,
name
FROM learning
</cfquery>
<cfoutput query="data_query">
<li>
<div class="list_div clearfix">
#id#: <input type="text" value="#name#">
</div>
</li>
</cfoutput>
Also you should properly 'scope' your query columns when outputting. This will make your code easier to maintain in future, e.g. you'll always know that #data_query.name# belonged to the query and wasn't some string set by some other piece of code somewhere. And it'll speed up page performance - if you don't scope variables (this applies to all types of variables, not just queries), then CF will loop through the different scopes until it finds something with this value. So by scoping, you prevent CF having to loop.
<cfquery name="data_query" datasource="#variables.dsn#">
SELECT
id,
name
FROM learning
</cfquery>
<cfoutput query="data_query">
<li>
<div class="list_div clearfix">
#data_query.id#: <input type="text" value="#data_query.name#">
</div>
</li>
</cfoutput>
On the whole your logic was fine.. just a few typos and minor changes needed..
Give this a try.
<cfquery name="data_query" datasource="#dsn#">
SELECT
id,
name
FROM learning
</cfquery>
<cfloop query="data_query">
<li>
<div class="list_div clearfix">
#id#: <input type="text" value="#name#">
</div>
</li>
</cfloop>
And if you didn't know about it:
<cfdump var="#data_query#">
OR
<cfdump var="#data_query#" abort>
Will give you a beautiful display of came back from your query, or in any variable or structure.

Preserving input value of type="file" in ColdFusion on postback

I have a form in ColdFusion that initially has 5 input fields for file uploading. Should the user realize they have more than 5 files to upload in the process of attaching them, I would like the form to preserve the values when it submits itself for the change in # of fields.
Using the <cfform> tag with the preservedata="yes" attribute is supposed to accomplish this - but all I'm getting is a temp value stored in the input's value on resubmit that isn't displayed in the field nor works for a submission.
edit: Thanks for the great answers everyone, you all helped and were correct. I was able to implement Adam's suggested solution. Works great! Thanks!
function changeFieldCount() // javascript function for submit on input count change
{
var count = document.forms[0].numtotal.options[document.forms[0].numtotal.selectedIndex].value;
document.forms[0].action = "me.cfm?count=" + count;
document.forms[0].submit();
}
<cfparam name="url.count" default="5">
<cfform name="dispfiles" method="post" enctype="multipart/form-data" preservedata="yes">
<!--- looping for file input fields --->
<cfloop index="counter" from="1" to="#url.count#">
<cfinput type="file" name="file#counter#" size="50" maxlength="60"><br>
</cfloop>
<br>Number of Files to Attach:
<!--- looping for # of input selection--->
<cfselect name="numtotal">
<cfloop index="cnt" from="1" to="20" step="1">
<cfoutput>
<option value="#cnt#" <cfif #cnt# eq #url.count#>selected</cfif>>
#cnt#
</option>
</cfoutput>
</cfloop>
</cfselect>
<cfinput type="button" name="op-display" value="Display" onclick="changeFieldCount()">
<cfinput type="button" name="op-upload" value="Attach Files" onclick="submitfrm(this.form)">
<cfinput type="button" name="cancel" value=" Cancel " onclick="window.close()">
</cfform>
This is what I'm getting when I view source on the resulting submission:
<input name="file1" id="file1" type="file" value=".../cfusion.war/neotmp8858718543274653167.tmp" maxlength="60" size="50" /><br>
This is by design in all browsers for security reasons. There is no way you can insert the value for a file field.
To elaborate on #SpliFF's answer, what you need to do is dynamically create more file fields with JavaScript.
Here's an example that uses jQuery to make the JavaScript a little easier. I've also used a variable to track the number of file fields displayed so that they all have a unique number appended to their names, making it possible to loop over and uniquely identify them server-side.
<script type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
//track the current number of file fields displayed
var numUploadFields = 5;
//attach an anonymous function to the button to add more fields
$(function(){
$("#add5").click(function(){
for (var i = 0; i < 5; i++){
numUploadFields += 1;
var newHTML = "<br/><input type='file' name='upload" +
numUploadFields + "' />";
$("#someSection").append(newHTML);
}
});
});
</script>
<form method="post" action="">
<div id="someSection">
<input type="file" name="upload1" /><br/>
<input type="file" name="upload2" /><br/>
<input type="file" name="upload3" /><br/>
<input type="file" name="upload4" /><br/>
<input type="file" name="upload5" />
</div>
<input type="button" id="add5" value="Add 5 more file fields" />
</form>
Build additional file inputs using JS DOM methods. Since you aren't leaving the page this is fast and nothing is lost.