Nested <cfloop> to display query results - <UL> or table? - algorithm

I'm trying to output a query set in chunks, in a wide horizontal band. I.e., given a resultset of 46 items grouped by 4 -
div1<br>1 2<br>3 4
<br>
div2<br>5 6<br>7 8
<br>
........
<br>
div12<br>45 46
How can I properly nest a couple of <CFLOOP> to properly output this, and would it be cleaner to do so as a table? The ultimate goal is to be able to jump(scroll) to each div as an anchor.
I'm having trouble coming up with a solution using either a table or an <UL>. Any pointers in the right direction would be appreciated!

Try Peter suggestion first, if that doesn't work, this algorithm should work.
<cfloop query="q">
<cfif (q.currentRow-1) % 4 EQ 0>
<br>div#q.currentRow\4+1#
</cfif>
<cfif q.currentRow % 2 EQ 1>
<br>
</cfif>
#q.currentRow#
</cfloop>
You should check for edge cases yourself.

Related

How can I select element base on string() in Xpath?

I have an XML like that:
<tr class="TREven">
<td class="Col0">
<span>
<b>Diary Compliance:</b>
Number of Daily Reports completed
<br/>
<i>
* Must be
<u>24</u>
or more
</i>
</span>
</td>
<td class="Col1">
<span class="Red">4 - Not eligible</span>
</td>
</tr>
I don't know how to select "4 - Not eligible" base on my input text (Diary Compliance: Number of Daily Reports completed * Must be 24 or more) which is contained by many child nodes of span before.
Could you help me?
Thanks,
This is alse my trouble
I often use this xpath to get element on cell 2 base on element on cell 1 in a row table like him:
//span[contains(text(),'Diary Compliance: Number of Daily Reports completed * Must be 24 or more')]/../..//span[contains(text(),'4 - Not eligible')]
It's ok until I get trouble when element on cell 1 has lot of format node, I can't pass my exactly text to element on cell 1. In my case, I must use exactly text, not contains text.

coldfusion performance comparison: custom tag versus function

On my test box I have ColdFusion 10 and have allocated just 1gig: "Maximum JVM Heap Size (in MB) = 1024".
I have a process that loops around 1000 times. Each loop iteration calls a custom tag ~200 times. So in all about 200,000 calls. The process never finishes, I eventually run out of memory. I'm sure it's what I'm doing inside the tags that is the problem and not the tags themselves. But I wanted to prove that. I ran this test and it seems that ColdFusion functions are much faster 2.7secs as opposed to 14.6secs. The test just increments a number.
My question is: Does this appear to be a valid test? If functions are so much faster should it be a rule of thumb to use them instead of custom tags whenever possible?
test results:
x = 0
Running good customtag 1000000 times...
x = 1000000
Time to complete: 14627
x = 0
Running good function 1000000 times...
x = 1000001
Time to complete: 2793
good.cfm custom tag:
<cfparam name="ATTRIBUTES.x" type="numeric" default="0">
<cfif thisTag.ExecutionMode eq "end">
<cfset request.x = ATTRIBUTES.x+1>
<cfset thistag.generatedcontent = "">
</cfif>
test code:
<cffunction name="good" output="false" returntype="Numeric" access="private">
<cfargument name="numIn" type="numeric" required="true">
<cfset var x = 0>
<cfset x = arguments.numIn + 1>
<cfreturn x>
</cffunction>
<cfset loopNum = 1000000>
<cfset request.x = 0>
<cfoutput>
x = #request.x#<br>
Running good customtag #loopNum# times...<br>
</cfoutput>
<cfset tBegin = GetTickCount()>
<cfloop from="1" to="#loopNum#" index="i">
<cf_good x="#request.x#"></cf_good>
</cfloop>
<cfset tEnd = GetTickCount()>
<cfset scriptTime = (tEnd - tBegin)>
<cfoutput>
x = #request.x#<br>
Time to complete: #scriptTime#<br>
</cfoutput>
<cfset request.x = 0>
<cfoutput>
x = #request.x#<br>
Running good function #loopNum# times...<br>
</cfoutput>
<cfset tBegin = GetTickCount()>
<cfloop from="1" to="#loopNum#" index="i">
<cfset request.x = good(i)>
</cfloop>
<cfset tEnd = GetTickCount()>
<cfset scriptTime = (tEnd - tBegin)>
<cfoutput>
x = #request.x#<br>
Time to complete: #scriptTime#<br>
</cfoutput>
You're comparing apples to oranges: custom tags and functions are not interchangeable concepts. Whilst they can both be coded to achieve much the same end, it would generally be a poor decision to use a custom tag where a function is appropriate; and equally a poor choice to use a function where a custom tag is a better fit.
Functions are for data manipulation; custom tags are for text / content manipulation. So your functions belong in your business logic, and your custom tags belong in your display logic.
Both have their place.
If you're looping over something 1000 times and calling some functionality 200 times per iteration... that really doesn't sound like you should be using a custom tag (it's not preparing text for output, is it?); it sounds like data-processing, so should be handled by functions (hopefully ones that are encapsulated in objects appropriately...)

Get count of specific nodes between two specific sibling nodes

I'm using HtmlAgilityPack to get a filtered DOM of <h2> and <h3> nodes and using Xpath 1.0 (from my Xpath 1.0 crash course this week) I need to get the number of <h3>'s (the number varies) that are between sibling <h2>'s as follows:
<div>
<h2>heading 1</h2>
<h3>sub 1.1</h3>
<h3>sub 1.2</h3>
<h2>heading 2</h2>
<h3>sub 2.1</h3>
<h2>heading 3</h2>
....
</div>
When I iterate (using C#) through the filtered nodes I want the exact number of <h3>'s that are after a <h2> and before the next <h2>. When I use the following I get all the <h3>'s as the result.
int countH3 = n.SelectNodes("./preceding-sibling::h2[2]/following-sibling::h2[3]/preceding-sibling::h3").Count(); //the [position] is set dynamically
For the node structure above would like the result of the code line to be:
countH3 = 1
but it is:
countH3 = 3
I've found many similar SO questions regarding "sibling nodes between sibling nodes" and have to thank #LarsH for his comment in another question that /preceding::h3 returns ALL <h3>'s which helped explain the issue. I think I may need to use the Kayessian method of node-set intersection but get the "invalid token" error when I include the . | union character as follows:
countH3 = n.SelectNodes("./h2[2]/following-sibling::h2[3]
[count(.|./h2[2]/following-sibling::h2[3]/preceding-sibling::h3)=
count(./h2[2]/following-sibling::h2[3]/preceding-sibling::h3)]").Count();
Any suggestions appreciated.

Pull Down with binding and AJAX

I am new to ColdFusion and just started learning about Ajax. The question I have is that I found on the web some cool coding to implement two pull down menus, where is the second one is dependent on what is selected from the first. The goal though is to pull all the values in the pull down list from a select query.
I am using the binding feature, which I just learned about. Everything in the first pull down is pulling correctly. The problem though is the second one. The error I am getting from AJAX logger is "Error invoking: Error Executing Database Query" Any advice would be greatly appreciated. Also thank you in advance for your help.
select.cfc
<cfcomponent output="false">
<!--- Get array of Catagory Description types --->
<cffunction name="cat_description" access="remote" returnType="array">
<!--- Define variables --->
<cfset var data="">
<cfset var result=ArrayNew(2)>
<cfset var i=0>
<!--- Get data --->
<cfquery name="getServiceCat" datasource="SOME_DATABASE">
SELECT DISTINCT CAT_DESC
FROM service_table
ORDER BY CAT_DESC
</cfquery>
<!--- Convert results to array--->
<cfloop index="i" from="1" to="#getServiceCat.recordcount#">
<cfset result[i][1]=getServiceCat.CAT_DESC[i]>
<cfset result[i][2]=getServiceCat.CAT_DESC[i]>
</cfloop>
<!--- And return it --->
<cfreturn result>
</cffunction>
<!--- Get Service Type by Cat description type --->
<cffunction name="getServiceType2" access="remote" returnType="array">
<cfargument name="CAT_DESC" type="string" required="true">
<!--- Define variables --->
<cfset var data="">
<cfset var result=ArrayNew(2)>
<cfset var i=0>
<!--- Get data --->
<cfquery name="getServiceType2" datasource="SOME_DATABASE" dbtype="query">
SELECT DISTINCT com_service_code, report_desc
FROM service_table
WHERE CAT_DESC = #ARGUMENTS.CAT_DESC#
ORDER BY report_desc
</cfquery>
</cfif>
<!--- Convert results to array --->
<cfloop index="i" from="1" to="#getServiceType2.recordcount#">
<cfset result[i][1]=getServiceType2.com_service_code[i]>
<cfset result[i][2]=getServiceType2.report_desc[i]>
</cfloop>
<!--- And return it --->
<cfreturn result>
</cffunction>
</cfcomponent>
The above is the cfc which handles all the queries. One of the main reason for this solution is the simplicity of the code that is required within the form.
...
<td>Select Category: <cfselect name="catdesc"
bind="cfc:select.cat_description()"
bindonload="true"/><br />
</td>
...
<td>Select Service: <cfselect name="service"
bind="cfc:select.getServiceType2({catdesc})"
bindonload="false"/>
</td>
I have searched for almost 2 days trying to find a solution to the query error. I know that the query works in SQL so I believe the issue has to do with AJAX and not correctly implementing the WHERE clause for the second pull down.
Again any advice or suggestions would be great. Additionally if you would do this solution another way I am more than happy to try that as well. As I stated I am very new to this language. Only 2 weeks in.
Testing CFC's
Always test cfc's in CF before plugging them into ajax. There is no point messing with bindings until after you have verified the cfc works without error, because if it does not work in CF, it is not going work with ajax either. The only difference is the errors will be harder to find.
As others suggested, start small. Test the query by itself. Then move onto to testing the CFC either using <cfinvoke> or simply invoke it from your browser with test values like:
http://yourserver/path/to/select.cfc?method=getServiceType2&cat_desc=
http://yourserver/path/to/select.cfc?method=getServiceType2&cat_desc=someValue
Error
In regards to the error, we need to see the full error message to provide more specific advice. However, looking at the query/code some likely causes are:
CAT_DESC is a varchar column, in which case your argument must be enclosed in single quotes. Otherwise the database will think the argument value is an object name (table or column).
WHERE CAT_DESC = '#ARGUMENTS.CAT_DESC#'
.. OR CAT_DESC is a numeric column, but your argument is empty. That would result in an invalid sql statement. You need to ensure a valid number is passed into the query (Or you could skip the WHERE clause when the argument is empty, depending on the desired results). One common approach is using the val() function to convert empty strings and other non-numeric values to zero, ie:
WHERE CAT_DESC = #val(ARGUMENTS.CAT_DESC)#
It also looks like you have a stray </cfif> after the second query. (Assuming it is not a copy/paste error)
One other thing, your second query specifies both datasource and dbtype. Those two attributes are mutually exclusive. Having both may cause a syntax error (I have not tried it). Either way you should only use one of them (most likely datasource).
SQL Injection
That said - the query above is vulnerable to sql injection. You should always use <cfqueryparam> on all variable query parameters to guard against sql injection. It has other benefits as well (performance, data type checking, etcetera). But sql injection protection is the most critical in a web application.
<!--- substitute the correct type for your db column --->
WHERE CAT_DESC = <cfqueryparam value="#ARGUMENTS.CAT_DESC#"
cfsqltype="cf_sql_varchar">
Improvements
As of CF8.0.1+, cfselect can bind to a query object (not just an array). So instead of building an array, simply return the raw query from the function.
<cffunction name="getServiceType2" access="remote" returnType="query">
<cfargument name="CAT_DESC" ....>
<cfset var data="">
<cfquery name="data" ..>
SELECT TheQueryColumnToDisplay, TheQueryColumnUsedForListValue
FROM ...
WHERE ...
</cfquery>
<cfreturn data>
</cffunction>
Then specify which column(s) to use for the display and value attributes:
<cfselect name="service"
bind="cfc:select.getServiceType2({catdesc})"
display="TheQueryColumnToDisplay"
value="TheQueryColumnUsedForListValue" ...>
My suggestion is to do one thing at a time. For your specific situation,
First, get your query to work with a cfquery tag.
Second, get it to work inside a function where you pass an argument to the function.
Next, put the function inside a cfc and call it from a cfc page using either or by creating an object and calling the function.
Finally do the bind.
This approach will make the errors more visible so that you can do something about them.
Other observations
use query parameters.
try to pass an id field to your query instead of a text description

Is it possible to dynamically populate a CFC with arguments?

The following code errors:
<cfdbinfo datasource="#Application.DSN#" name="getCols" type="columns" table="#this.tableName#">
<cftry>
<cfquery name="getColumnDetails" dbtype="query">
SELECT COLUMN_NAME,TYPE_NAME
FROM getCols
WHERE IS_PRIMARYKEY = 'NO'
</cfquery>
<cfcatch>
<cfset this.ErrorState = true>
<cfthrow message="General DB Error">
</cfcatch>
</cftry>
<cfloop query="getColumnDetails">
<cfargument name="#getColumnDetails.COLUMN_NAME#" displayName="values" type="Any" required="false" />
</cfloop>
but I would really like to know if it is possible to dynamically set the arguments for a CFC — or is it better to simply pass in a struct and deal with that?
Thanks
Rob
Unlikely.
Two ways, as you said, don't define the cfargument tags and instead look for them being passed in with StructKeyExists(ARGUMENTS, aDynamicName) or, create a code generator and write these methods to a file.
One way I've tried to do similar things to what you're doing is something along these lines:
<cffunction name="doSomethingWithDatabase">
<cfargument name="potentialColumns" type="string">
<cfargument name="columnValues" type="struct">
and then loop over the list of potential columns, using each element in the list as the index to search for in the columnValues struct. if that value exists in the struct, then yo'ure good; otherwise, you ignore that column in the update.
you'd then call the function something like this:
to get the columns you're looking for
alternately, you could ignore the potentialColumns argument and just get that information in your cfc:
<cffunction name="doSomethingWithDatabase">
<cfargument name="columnValues" type="struct">
<cfset potentialColumns = getMyColumns()>
.... loop....

Resources