CFloop within a loop [closed] - coldfusion

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
I want to loop through a query and for every row iteration to display two ids per row. I have tried a loop within a loop, but it just outputs all of the rows twice.
<!--- sample query --->
<cfset sheets = queryNew("")>
<cfset queryAddColumn(sheets, "id", [1,2,3,4])>
<table>
<cfoutput>
<cfloop query="sheets">
<tr>
<cfloop query="sheets">
<td align="center">#sheets.id#</td>
</cfloop>
</tr>
</cfloop>
</cfoutput>
</table>

Nested loops are not the way to do this. Conditional logic with the mod function is better. Here is a simple example.
<cfloop query = "sheets">
<tr>
td cells and data go here:
<cfif currentrow mod 2 is 0>
</tr>
<tr>
</cfif>
Note that this answer is intentionally incomplete. The part I am leaving for you is how to handle the situation where the query has an odd number of rows.

Related

ColdFusion form - display error details from database table

I have a database table with error id and error details for form validation errors.
In my ColdFusion form page, I have a list of error numbers 2,4,7 available. But I want to display errors like "Please enter name" etc.
I want to compare my list of errors with error id's in the database table and display the corresponding error in my form. Please let me know if there is any better way to do it. Thanks in advance!!
<form name = "empform" action="empform.cfm" method="post">
<cfoutput>
<table border="0">
<tr><td colspan="4" align="center" style="padding-bottom:20px;"><h2>Add Employee</h2></td></tr>
<cfif (structKeyExists(rc,"addemp"))>
<cfif rc.result.hasErrors()>
<tr>
<td colspan="4" style="border:2px solid red;">
Please review the following:
<ul>
<cfloop array="#rc.result.getFailureMessages()#" index="message">
<li>#message#</li>
</cfloop>
</ul>
</td>
</tr>
</cfif>
</cfif>
<cfset myArray = rc.result.getFailureMessages()>
<cfset myList = ArrayToList(myArray, ",")>
<cfquery name="qErrorMessages" datasource="#dsn#" >
Select * from ErrorMessages
</cfquery>
<tr>
<td><label><b>Emp Last Name</b></label></td>
<td><input name="lastname" type="text" value="" /></td>
<td><label><b>First Name</b></label></td>
<td><input name="FirstName" type="text" value="" /></td>
</tr>
<tr>
<td><label><b> Emp Birth Date</b></label></td>
<td><input name="birthdate" type="text" value="" /></td>
<td><label><b>Salary</b></label></td>
<td><input name="salary" type="text" value="" /></td>
</tr>
<tr><td style="padding-top:10px;">
<input type="submit" name = "addemp" value ="AddEmp /></td>
</tr>
</table>
</cfoutput>
</form>
I can display errors as shown above
Now I want to show actual messages. I am stuck up here.
My issue is not solved. I am able to loop through the table of errors only. Probably I need to loop through my list of error ids and then I need another loop to look up for the table errorId's to match then display the error.
I got it fixed with the below code.
<cfloop index="ind" list=#mylist#>
<cfquery datasource= "#dsn#" name="emperrors">
Select errorid,errmessage from errorcodes where errorid = #ind#
</cfquery>
#emperrors.errmessage#<br>
</cfloop>
Your question is short on details, but I'd probably query the table with the error messages, convert it to a struct, then drop that struct into a persistent scope that I could then reference later.
Something like:
<cfset errorStruct={}>
<cfloop query="qFormErrorMsgs">
<cfset errorStruct[error_id]=error_msg>
</cfloop>
<cfset application.errorStruct=errorStruct>
Then, when you want to display the error:
<p>Please fix the following error: #application.errorStruct[variables.error_id]#</p>
But that's just one way you might do it. Without more information in your question all you're going to get is guess (if you're lucky).
Alright, your answer detailing code should have been appended to your question via an edit, and still that's really not enough code. How are you determining your errors?
It sounds like your errors are something like this:
Please review the following:
2
3
6.
When what you want is error messages.
An easy way to update this is to load all your messages into an array where the index corresponds to the error codes.
<cfset ErrDetails=ArrayNew()>
<cfset ErrDetails[1]="You didn't enter your first name.">
<cfset ErrDetails[2]="You didn't enter your last name.">
<cfset ErrDetails[3]="You didn't enter a properly formatted birth date.">
<cfset ErrDetails[4]="It appears, from your birthdate, that your age is below 18">
And then, in your cfloop, you can
<cfloop array="#rc.result.getFailureMessages()#" index="message">
<li>#ErrDetails[message]#</li>
</cfloop>
Lastly it sounds like your errorcodes/messages are coming from a database. I'm not really sure that's necessary, but if you really want to keep it that way, that's fine. You can initialize this into the application scope so that you can call it later without rerunning the query, or just put it somewhere in flat text. However, if you want the application scope answer, here's a method.
<cflock scope="application" timeout="5">
<cfif not isDefined("application.ErrDetails")>
<cfquery name="getec">
select ErrID,ErrMessage from ErrorCodes
</cfquery>
<cfset Application.ErrDetails = []>
<cfloop query="getec">
<cfset Application.ErrDetails[ErrID]=ErrMessage>
</cfloop>
</cfif>
<cfset request.ErrDetails = Application.ErrDetails>
</cflock>
Once this is initiated, the cfloop displaying errors as I showed above can use #request.errDetails[message]#
If it's just running in one page, I wouldn't worry about creating an application variable. I'd just hardcode it in, or do a query there with a cfloop similar to above writing to a variables scope variable, not an application variable so the cflock isn't needed.
Unless you also have web based admin creating these rules, I'd probably ditch the table altogether and drop it directly into code, a cfinclude, a udf, or a custom tag.
Since the asker has updated their question with an answer.
Your answer needlessly repeats the query for each iteration.
Try something like this..
<cfquery datasource="#dsn#" name="GetECs">
select ErrorID,ErrMessage from ErrorCodes
where ErrorID in (<cfqueryparam cfsqltype="cf_sql_integer" list="yes" value="#mylist#">)
</cfquery>
<cfoutput query="GetECs">
#currentrow#. #ErrMessage#<br>
</cfoutput>
Notes:
One query is run.
For results, the where ErrorID in (<cfqueryparam cfsqltype="cf_sql_integer" list="yes" value="#mylist#">) is the same as saying where ErrorID in (#mylist#). However, the tag secures your queries against sql injection (the tactic of modifying variables to produce undesirable/malicious results in a query. Read up on cfqueryparam at http://help.adobe.com/livedocs/coldfusion/8/htmldocs/help.html?content=Tags_p-q_18.html

Nested cfloops with less records than outer loop cause "array index out of range" error

I'm very curious why this is happening. I've run into it twice now, and after a ton of googling/so'ing, I haven't found any reason I actually understand. The gist of it:
Query 1: selectContent (6 records; no blanks/nulls etc)
Query 2: selectPricing (5 records; no blanks/nulls etc)
Output:
<cfloop query="selectContent">
<section>
#selectContent.h2#
<cfif selectContent.id eq 3>
<cfloop query="selectPricing" group="groupCol">
<table class="pricing">
<thead>
<tr>
<th>#description#</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<cfloop>
<tr>
<td>#selectPricing.description#</td>
<td>#selectPricing.price#</td>
</tr>
</cfloop>
</tbody>
</table>
</cfloop>
</cfif>
#selectContent.content#
</section>
</cfloop>
This will give the following error: Array index out of range: 5
The error only occurs when the second query has less records than the first. Essentially it feels like that first cfloop takes over the loop iteration from that second one and this causes the issue, but also only if you have that third grouped cfloop in there. The entire inner cfloop runs, as is there in the source.
I've come up with two ways to resolve this:
do this with cfoutput/group, but that's relatively ugly as it means lots of closing of cfoutputs from other parts of the page.
stick a cfbreak on that third cfloop if the currentRow matches the recordcount.
So, two questions:
Why is this even happening?
Should I be using a totally different approach here (the fact that googling/so'ing isn't finding others with this issue certainly seems to imply that...)?
EDIT
I've filed this as a Coldfusion bug based on Adam Cameron's feedback below. Bug #3820049
Well done, you've found a bug in CF. I can replicate it (PS... it'd've been cool had you included some sample data save me having to do it!)
The work-around is straight forward though:
<cfscript>
selectContent = queryNew("h2,id,content", "varchar,integer,varchar", [
["one", 1, "content.1"],
["two", 2, "content.2"],
["three", 3, "content.3"],
["four", 4, "content.4"],
["five", 5, "content.5"],
["six", 6, "content.6"],
["seven", 7, "content.7"]
]);
selectPricing = queryNew("groupCol,description,price", "varchar,varchar,varchar", [
["groupCol.1", "description.1", "1.11"],
["groupCol.2", "description.2", "2.22"],
["groupCol.2", "description.3", "3.33"],
["groupCol.3", "description.4", "4.44"],
["groupCol.3", "description.5", "5.55"],
["groupCol.3", "description.6", "6.66"]
]);
</cfscript>
<cfloop query="selectContent">
<section>
<cfoutput>#selectContent.h2#</cfoutput>
<cfif selectContent.id eq 3>
<cfoutput query="selectPricing" group="groupCol">
<table class="pricing">
<thead>
<tr>
<th>#description#</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<cfoutput>
<tr>
<td>#description#</td>
<td>#price#</td>
</tr>
</cfoutput>
</tbody>
</table>
</cfoutput>
</cfif>
<cfoutput>#selectContent.content#</cfoutput>
</section>
</cfloop>
Note how I've used <cfoutput> to do the inner looping.
This is a serious bug in ColdFusion (10 and 11), and you should raise it on their bug base (if you do, report the ticket number/URL back here so we can vote on it)

Next Page, for data editing

I did SEARCHing code, something like this.
--pageone.cfm--
<cfparam name="Form.studNo" default="" />
<form action="pagetwo.cfm" method="POST">
<label> Please insert ID:
<input name="studNo" value="<cfoutput>#Form.studNo#</cfoutput>" />
</label>
<input type="submit" value="Search" />
</form>
--pagetwo--
SELECT * FROM students
WHERE students_ID IN (#Form.studNo#)
On page two, I will display the details of student's info and user can edit a new information if the data is not right.
I'm thinking of, displaying data of one student per page and user can click Next for the next students (ID that has been inserted) on pageone.cfm
Can anyone help me with these?
#henry.
I did try something like this.
--example.cfm--
<CFPARAM NAME="StartRow" DEFAULT="1">
<CFPARAM NAME="DisplayRows" DEFAULT="1">
<CFQUERY NAME="getStudent" DATASOURCE="#dsn#"
CACHEDWITHIN="#CreateTimeSpan(0,0,15,0)#">
SELECT *
FROM students
</CFQUERY>
<CFSET ToRow = StartRow + (DisplayRows - 1)>
<CFIF ToRow GT getStudent.RecordCount>
<CFSET ToRow = getStudent.RecordCount>
</CFIF>
<HTML>
<HEAD>
<TITLE>Next/Previous Record Browsing</TITLE>
</HEAD>
<BODY>
<CFOUTPUT>
<H4>Displaying records #StartRow# - #ToRow# from the
#getStudent.RecordCount# data inserted.</H4>
</CFOUTPUT>
<!--- create the header for the table --->
<TABLE CELLPADDING="3" CELLSPACING="0">
<TR BGCOLOR="#888888">
<TH>Name</TH>
<TH>ID</TH>
<TH>Gender</TH>
<TH>E-mail</TH>
</TR>
<CFOUTPUT QUERY="getStudent" STARTROW="#StartRow#"
MAXROWS="#DisplayRows#">
<TR BGCOLOR="##C0C0C0">
<TD>#Name#</TD>
<TD>#ID#</TD>
<TD>#Gender#</TD>
<TD>#Email#</TD>
</TR>
</CFOUTPUT>
</TABLE>
<CFSET Next = StartRow + DisplayRows>
<CFSET Previous = StartRow - DisplayRows>
<!--- Create a previous records link if the records being displayed aren't the
first set --->
<CFOUTPUT>
<CFIF Previous GTE 1>
<A HREF="example.cfm?StartRow=#Previous#"><B>Previous #DisplayRows#
Records</B></A>
<CFELSE>
Previous Records
</CFIF>
<CFIF Next LTE getStudent.RecordCount>
<A HREF="example.cfm?StartRow=#Next#"><B>Next
<CFIF (getStudent.RecordCount - Next) LT DisplayRows>
#Evaluate((getStudent.RecordCount - Next)+1)#
<CFELSE>
#DisplayRows#
</CFIF> Records</B></A>
<CFELSE>
Next Records
</CFIF>
</CFOUTPUT>
</BODY>
</HTML>
You may use session to store the specified IDs in pageone.cfm, then go through the array of IDs and use array index as the current page number, and array length as the total page count.
However in this day and age, using JS to display a record at a time makes more sense.
Your approach is ok, but I'd recommend using existing libaries that do the trick of paginating and even constructing HTML for displaying page number buttons, next-previous links and what not.
Please revise http://paginationcfc.riaforge.org/ - a ColdFusion project that deals with pagination quite well and is very easy to implement, saving you tons of work. You could even set it do display one record per page, and the rest (next-previous buttons and maybe "page numbers") would be automagically done by the pagination component.
Ah, doing so (one big query and subsequent sub-queries) I strongly suggest caching.
HTH.

Need advice.. dynamic td tr

Hi am doing dynamic TR and TD in coldfusion, but apparently i am trying this kind of screen. here is my try with colfusion
<cfif variables.newrow EQ true>
<tr align="center">
</cfif>
<cfoutput query="gResults">
<cfquery datasource="#request.dsn#" name="nextqueries">
Query
</cfquery>
<td height="30" valign="middle"><strong>
<a href="viewdir.cfm?catID=#val(pic_id)#
<cfif isDefined('url.l') and url.l neq ''>&l=#url.l#</cfif>">
#pic_cat_name#</a></strong><br>
<cfloop query="nextqueries">
<cfquery datasource="#request.dsn#" name="showanother">
select * from
mytable as grlist
where pic_cid =
<cfqueryparam cfsqltype="cf_sql_numeric" value="#Trim(nextqueries.pic_id)">
</cfquery>
»
<a href="viewdir.cfm?catID=#val(pic_id)#
<cfif isDefined('url.l') and url.l neq ''>&l=#url.l#</cfif>">#pic_cat_name#
</a> </cfloop></td>
<cfif gResults.currentRow MOD 4 EQ 0>
</tr>
<cfset variables.newrow = true>
<cfelse>
<cfset variables.newrow = false>
</cfif>
</cfoutput>
trying to do like this:
http://screencast.com/t/oso4jkhBm3
There are a lot of potential improvements, but this answer will only deal with table rows. You start with:
<cfif variables.newrow EQ true>
<tr align="center">
</cfif>
Now I'm going to look for the closing tag. The only one I see is here:
<cfif gResults.currentRow MOD 4 EQ 0></tr></cfif>
and that line is inside a query loop. What this means is that you might have more than one closing tag, or you might not have any. You need exactly one. To solve this specific problem, you have to do this:
<cfif variables.newrow EQ true>
<tr align="center">
code to populate this table row
</cfif>
When you get that part sorted out, we can look at what goes into the details.
To get data to display in an html table from top to bottom then left to right like in the image linked in your question, you can do something similar to the following.
<!--- Get query results --->
<cfset arrayOFValues = arraynew(1)>
<cfset queryResults = querynew("Col1,Col2,Col3")>
<!--- Fill query with example data --->
<cfloop from="1" to="25" index="i">
<cfset queryaddrow(queryResults)>
<cfset querySetCell(queryResults, "Col1", "Col1 Row " & i)>
<cfset querySetCell(queryResults, "Col2", "Col1 Row " & i)>
<cfset querySetCell(queryResults, "Col3", "Col1 Row " & i)>
</cfloop>
<!--- Now have a query named queryResults with 25 rows --->
<!--- Set the number of columns and calculate the number of rows needed --->
<cfset numberOfColumns = 3>
<cfset rowsNeeded = ceiling(queryResults.recordcount / numberOfColumns)>
<cfoutput>
<table>
<cfloop from="1" to="#rowsNeeded#" index="curRow">
<tr>
<cfloop from="0" to="#numberOfColumns-1#" index="curCol">
<td>
#queryResults.Col1[(rowsNeeded * curCol) + curRow]#
</td>
</cfloop>
</tr>
</cfloop>
</table>
</cfoutput>
The first part is just creating a query result. I then find the number of rows that is needed to display the records by dividing the number of results returned in the query by the number of columns to be displayed. The ceiling is required for when the result is not a whole number.
We have to loop each column record with in the row to get the desired index. To find the required index of the field we have to take the row that’s being displayed + the column were on times the the number of rows that will be displayed.
If you know the number of columns you can hard code for them in the following manner and eliminate the inner loop.
<tr>
<td>#queryResults.Col1[3 * rowsNeeded]#</td>
<td>#queryResults.Col1[3 + 1*rowsNeeded]#</td>
<td>#queryResults.Col1[3 + 2*rowsNeeded]#</td>
</tr>

how to loop through Query Columns in ColdFusion

I have a query in a my CFC. The function contains a simple query as such.
<cfquery name="qrySE" datasource=#mydatasource#>
SELECT
NAMES,SALARY
FROM tblTest
</cfquery>
I want to display my resultset as such (horizontally):
NAME1 NAME2 NAME3 NAME4
10 20 45 62
Is there a way to loop through the columns of my query and create a virtual query for this purpose?
If anyone has done this, please let me know.
Just wanted to add Al Everett's solution returns the columns back in alphabetical order. If you would like to get the column names back in the same order as the query you can use:
ArrayToList( qrySE.getColumnNames() )
which I found here: http://www.richarddavies.us/archives/2009/07/cf_columnlist.php
you can use this to create a function to output queries to a table like this:
<cffunction name="displayQueryAsTable" output="true">
<cfargument name="rawQueryObject" type="query" required="true">
<table >
<tr>
<cfloop list="#ArrayToList(rawQueryObject.getColumnNames())#" index="col" >
<th>#col#</th>
</cfloop>
</tr>
<cfloop query="rawQueryObject">
<tr>
<cfloop list="#ArrayToList(rawQueryObject.getColumnNames())#" index="col">
<td>#rawQueryObject[col][currentrow]#</td>
</cfloop>
</tr>
</cfloop>
</table>
</cffunction>
You could use the built-in query.columnList that is returned with each query. (It's metadata of the query, like recordCount.)
You could do something like this:
<table>
<cfloop list="#qrySE.columnList#" index="col">
<tr>
<cfloop query="qrySE">
<td>#qrySE[col][currentRow]#</td>
</cfloop>
</tr>
</cfloop>
</table>
Not tested, but that should give you the idea.