using autocomplete with commas in a field - coldfusion

I'm trying to use autocomplete with a field with commas in it. When I type the comma it will ignore it, and won't return anything. So far I have this:
index.cfm
<!--- A simple form for auto suggest --->
<cfform action="autosuggest.cfm" method="post">
Artist:
<cfinput type="text" name="artist" size="50" autosuggest="cfc:autosuggest.findartist({cfautosuggestvalue})" autosuggestminlength="4" maxresultsdisplayed="5" /><br /><br />
</cfform>
autosuggest.cfc
<cfcomponent output="false">
<!--- Lookup used for auto suggest --->
<cffunction name="findartist" access="remote" returntype="string">
<cfargument name="search" type="any" required="false" default="">
<!--- Define variables --->
<cfset var local = {} />
<!--- Query Location Table --->
<cfquery name="local.query" datasource="#application.datasource#" >
select DISTINCT artist
from items
where artist like <cfqueryparam cfsqltype="cf_sql_varchar" value="#ucase(arguments.search)#%" />
order by artist
</cfquery>
<!--- And return it as a List --->
<cfreturn valueList(local.query.artist)>
</cffunction>
</cfcomponent>
When I try to search for example Brown,James it doesn't return anything. What do I need to put in it to return results with commas.
Thanks

One option is to have your function return an array, instead of a string. Then delimiters are not an issue.
<cffunction name="findartist" access="remote" returntype="array">
...
<cfreturn listToArray(valueList(local.query.artist, chr(30)), chr(30))>
</cffunction>
Update:
As Raymond pointed out, the only sure-fire way to avoid delimiter issues on the CF side is not to use them. Instead loop through the query to build the array, ie:
<cffunction name="findartist" access="remote" returntype="array">
...
<cfset local.arr = []>
<cfloop query="local.query">
<cfset arrayAppend(local.arr, local.query.artist)>
</cfloop>
<cfreturn local.arr>
</cffunction>

Related

Coldfusion autosuggest not working for suggestions in the middle of the string

For my autosuggest I'm using the code below.
When typing, it only looks at the beginning of the string.
For example if I type 'abcd' it will come with suggestions 'abcde...' that is good.
But what it doesn't do, if I type 'def' and in the database I have a string 'abcdefg' then this doesn't come in the autosuggest.
In my query I do have the percent characters infront and behind the search string.
So to me it looks like the autosuggest only looks at the characters typed at the beginning, not in the middle.
Or am I doing something wrong?
<cfcomponent output="false">
<cffunction name="lookupJobs" access="remote" returntype="array">
<cfargument name="search" type="any" required="false" default="">
<cfset var qGetJobs="">
<cfset var result=ArrayNew(1)>
<cfquery name="qGetJobs" datasource="#application.dsn#">
SELECT DISTINCT title
FROM tbl_jobs
WHERE title LIKE '%#arguments.search#%'
ORDER BY title
</cfquery>
<cfloop query="qGetJobs">
<cfset ArrayAppend(result, title)>
</cfloop>
<cfreturn result>
</cffunction>
</cfcomponent>
Thanks to Leigh, the solution was with the cfinput.
<cfinput type="text" name="title"
showautosuggestloadingicon="false"
autosuggest="cfc:autosuggest.lookupJobs({cfautosuggestvalue})"
matchContains="true">
I needed to add the attribute: matchContains="true"

Coldfusion function within conditional logic

I am having an issue with functions inside my cfc. They are acting funny when I try to introduce conditional logic to assign a query to the grid. Basically in the URL I will have ?GRIDID=x and it will tell the cfc which function to run, but when I nest the closing cffunction tag inside of the if statements, it throws an error. Here is the code.
<cffunction name="grabInfo" access="remote" output="false" returntype="any">
<cfargument name="page" required="yes">
<cfargument name="pageSize" required="yes">
<cfargument name="gridsortcolumn" required="yes">
<cfargument name="gridsortdirection" required="yes">
<cfargument name="filtercolumn" required="no" default="">
<cfargument name="filter" required="no" default="">
<cfargument name="gridID" required="yes">
<cfif arguments.gridsortcolumn eq "">
<cfset arguments.gridsortcolumn = "PatientsName" />
<cfset arguments.gridsortdirection = "asc" />
</cfif>
<cfif ARGUMENTS.gridID EQ "1">
<cfquery name="x" datasource="#dsn#">
<!--- .... --->
</cfquery>
<cfreturn QueryConvertForGrid(qGrabInfo, Arguments.page, Arguments.pagesize)>
</cffunction>
</cfif>
<cfif ARGUMENTS.gridID EQ "2">
<cfquery name="x" datasource="#dsn#">
<!--- .... --->
</cfquery>
<cfreturn QueryConvertForGrid(qGrabInfo, Arguments.page, Arguments.pagesize)>
</cffunction>
</cfif>
This will give me the error Context validation error for the cfif tag. but as you can see, all the cfif statements are closed. If I take the first argument and place it with the closing cffunction tag outside of the if statement it will work, like so
<cfif ARGUMENTS.gridID EQ "1">
<cfquery name="x" datasource="#dsn#">
<!--- .... --->
</cfquery>
<cfreturn QueryConvertForGrid(qGrabInfo, Arguments.page, Arguments.pagesize)>
</cfif>
<cfif ARGUMENTS.gridID EQ "2">
<cfquery name="x" datasource="#dsn#">
<!--- .... --->
</cfquery>
<cfreturn QueryConvertForGrid(qGrabInfo, Arguments.page, Arguments.pagesize)>
</cfif>
</cffunction>
The reason I need to do this is because I need several other functions run when GridID EQ 2 as well, so I need to close the function and open another as follows
<cfif ARGUMENTS.gridID EQ "2">
<cfquery name="x" datasource="#dsn#">
<!--- .... --->
</cfquery>
<cfreturn QueryConvertForGrid(qGrabInfo, Arguments.page, Arguments.pagesize)>
</cffunction>
<cffunction name="otherFunction">
<!--- .... --->
</cffunction>
</cfif>
Add additional functions in your component.
<cffunction name="grabInfo" access="remote" output="false" returntype="any">
<cfargument name="page" required="yes">
<cfargument name="pageSize" required="yes">
<cfargument name="gridsortcolumn" required="yes">
<cfargument name="gridsortdirection" required="yes">
<cfargument name="filtercolumn" required="no" default="">
<cfargument name="filter" required="no" default="">
<cfargument name="gridID" required="yes">
<cfif arguments.gridsortcolumn eq "">
<cfset arguments.gridsortcolumn = "PatientsName" />
<cfset arguments.gridsortdirection = "asc" />
</cfif>
<cfif ARGUMENTS.gridID EQ "1">
<cfquery name="x" datasource="#dsn#">
<!--- .... --->
</cfquery>
<cfreturn QueryConvertForGrid(qGrabInfo, Arguments.page, Arguments.pagesize)>
</cfif>
<cfif ARGUMENTS.gridID EQ "2">
<cfquery name="x" datasource="#dsn#">
<!--- .... --->
</cfquery>
<!--- call your other functions --->
<cfset otherFunction(arg1, arg2)>
<cfset anotherFunction(arg1, arg2)>
<cfreturn QueryConvertForGrid(qGrabInfo, Arguments.page, Arguments.pagesize)>
</cfif>
</cffunction>
New functions in same component
<cffunction name="otherFunction" access="remote" output="false" returntype="any">
<cfargument name="arg1">
<cfargument name="arg2">
<!--- do things --->
</cffunction>
<cffunction name="anotherFunction" access="remote" output="false" returntype="any">
<cfargument name="arg1">
<cfargument name="arg2">
<!--- do things --->
</cffunction>
The chief problem you have is a lack of understanding how code compiles. Your code is not executed at runtime, which is when stuff like conditions are evaluated, it needs to be compiled first. And to be compiled, the code needs to be syntactically valid. Which yours is not.
Functions are discrete units of processing, and need to be self contained. What you're trying to do makes absolutely no sense at all from a code perspective. It also demonstrates a lack of understanding of how functions work. They do not execute when they are declared (ie: the <cffunction>/</cffunction> block, they run when they're called.
Matt has got you on the right track, but to reiterate, you don't do this:
<cffunction name="mainFunction">
<!--- some stuff --->
<cfif someCondition>
<!--- some other stuff --->
<!--- finish off --->
</cffunction>
<cfelse>
<!--- different stuff --->
<cffunction name="theOtherFunction">
<!--- different function --->
</cffunction>
</cffunction><!--- this is for the outer function --->
</cfif>
That's... well it's not right.
What you want is this:
<cffunction name="mainFunction">
<!--- some stuff --->
<cfif someCondition>
<!--- some other stuff --->
<!--- different function --->
<cfset something = theOtherFunction()>
</cfif>
<!--- finish off --->
</cffunction>
<cffunction nname="theOtherFuction">
<!--- different stuff --->
</cffunction>
Note how each coding structure is self-contained.
I think you could benefit from reading through the CFML docs, and also some basic programming tutorials (any language) before you go too much further into it.
Also note: try to avoid using tag-based code for business logic: tags are really better suited for views, and it's a throw-back to earlier, ill-informed times that one can even define a function with tags. As a rule of thumb: tags for views; script for logic.
It's also important to read and understand the comments below. Even if your code was syntactically correct and could compile, it still wouldn't do what you want it to do because functions are compiled separately to the rest of the code, so your conditionality still wouldn't work.

ColdFusion autosuggest in text field is not responsive

I have a text field that I want to autosuggest values based on a query. I have a main file along with a separate file (getdata.cfc) that holds my query.
Here is the text field portion of my main file:
<cfinput name="search_query" autosuggest="url:getdata.cfc?suggestvalue={cfautosuggestvalue}" maxResultsDisplay="10" showAutoSuggestLoadingIcon="true" size="10" />
Here is the code in getdata.cfc:
<cfcomponent>
<cffunction name="get_data" access="remote" output="false">
<cfargument name="suggestvalue" required="true">
<cfquery name="get_data" datasource="#application.DSN#">
SELECT DISTINCT myItem
FROM myTable
WHERE myItem LIKE <cfqueryparam value="#suggestvalue#%"
cfsqltype="cf_sql_varchar">
ORDER BY myItem
</cfquery>
<cfif get_data.recordCount eq 1>
<cfreturn ",#get_data.myItem#">
<cfelse>
<cfreturn ValueList(get_data.myItem)>
</cfif>
</cffunction>
</cfcomponent>
The text field shows up fine, but when I type a word no autosuggest values show up. Nothing happens. The text is just displayed as I type it.
Any suggestions? Thank you!
I switched away to using jquery plugins from a lot of CF stuff, but here is an example I have that works in some old production code
<cfinput type="text" name="email" id="email" autosuggest="cfc:cfc.users.lookupEmail({cfautosuggestvalue})" maxresultsdisplayed = "25">
<cffunction name="lookupEmail" access="remote" returntype="array">
<cfargument name="search" type="any" required="false" default="">
<!--- Define variables --->
<cfset var data="">
<cfset var result=ArrayNew(1)>
<!--- Do search --->
<cfquery name="data" datasource="datasource" maxrows="25" cachedwithin="#CreateTimeSpan(0,0,30,0)#">
SELECT distinct email
FROM users
WHERE email LIKE <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.search#%">
ORDER BY email
</cfquery>
<!--- Build result array --->
<cfloop query="data">
<cfset ArrayAppend(result, email)>
</cfloop>
<!--- And return it --->
<cfreturn result>
</cffunction>
maybe this helps
also make sure you have your cfform tags around your form, and make sure that your /cfide folder is mapped to your website.
looking at your code and comparing it... it may be the way your calling the cfc (filename)
try: autosuggest="cfc:getdata.get_data.({cfautosuggestvalue})"

Binding Data to a CFTEXTAREA

I am populating a cfselect box using the bind attribute. I'd like to be able to make my selection from the two drop downs and then populate a CFTEXTAREA based on that selection. I thought I could just issue a similar bind attribute command but the textarea outputs:
[object Object]
CF8 documentation says that this is possible? What am I missing?
Form Code:
<cfselect name="descriptionDD" value="description" bind="cfc:cfcs.menudata.getData()" bindonload="true" />
<cftextarea name="detailDD" value="service_detail" bind="cfc:cfcs.menudata.getDetail({descriptionDD})" bindonload="true" />
CFC Code:
<cfcomponent>
<!---First Select Box --->
<cffunction name="getData" access="remote" returntype="query">
<!--- Function to get data from datasource --->
<cfquery name="data" datasource="ezpay">
select description
from service_descriptions
order by description
</cfquery>
<!--- Return results --->
<cfreturn data>
</cffunction>
<!---Second Select Box --->
<cffunction name="getDetail" access="remote" returnType="query">
<cfargument name="description" type="any" required="true">
<cfquery name="dataDetail" datasource="ezpay">
SELECT service_detail
from service_descriptions
WHERE description = '#ARGUMENTS.description#'
ORDER BY ID ASC
</cfquery>
<cfreturn dataDetail>
</cffunction>
</cfcomponent>
but the textarea outputs: [object Object]
That usually means you are returning a complex object where a simple string is expected instead. [object Object] is essentially the string representation of the complex object (in your case a query).
TextArea's are designed to display a single value. So your bind function should return a string, not a query object.
<cffunction name="getDetail" access="remote" returnType="string">
<cfargument name="description" type="any" required="true">
<!--- localize function variables --->
<cfset var dataDetail = "">
<cfquery name="dataDetail" datasource="ezpay">
SELECT service_detail
FROM service_descriptions
<!--- adjust cfsqltype if needed --->
WHERE description = <cfqueryparam valu="#ARGUMENTS.description#" cfsqltype="cf_sql_varchar">
ORDER BY ID ASC
</cfquery>
<cfreturn dataDetail.service_detail>
</cffunction>

returning multiple stored procedure result sets from a cfc

I am trying to convert some pages from my app to use cfc's, and one page uses a stored procedure to retrieve a couple sets of data.
Now when I access the results, they act just like a if I used a <cfquery> tag, and all of the functionality that gives. So now I am trying to use this same stored procedure in a cfc that I am building, and I would like to be able access the results in the same manner, and there in lies my problem. I'm not sure how to return multiple queries from the function, without creating an array, which I have started. By the way, the function is incomplete. I was just trying to get something to work. In the below setup I get an array of query objects, but I feel there is a better way to do it.
Here is the <cffuntion>:
<cffunction name="getProfileData"
access="public"
output="false"
returntype="string">
<cfargument name="cusip" type="string" required="true">
<cfargument name="report_date" type="date" required="true">
<cfset var errorMessage = "everything is good">
<cftry>
<cfstoredproc datasource="#dsn#" procedure="prc_asset_profile_retrieve">
<cfprocparam type="in" cfsqltype="cf_sql_varchar" value="#cusip#" dbvarname="#cusip">
<cfprocparam type="in" cfsqltype="cf_sql_varchar" value="#report_date#" dbvarname="#reportDate">
<cfprocresult name="profile_head" resultset="1">
<cfprocresult name="attribution" resultset="2">
<cfprocresult name="characteristics" resultset="3">
<cfprocresult name="exposure" resultset="4">
<cfprocresult name="weights" resultset="5">
<cfprocresult name="holdings" resultset="6">
</cfstoredproc>
<cfset var profileArray = []>
<cfset #ArrayAppend(profileArray,profile_head)#>
<cfcatch type="any">
<cfset errorMessage = "something happened">
</cfcatch>
</cftry>
<cfreturn profileArray>
</cffunction>
When I output some test data, it matches up
<cfset count = fund_profile.getProfileData("#cusip#","#report_date#")>
<cfdump var="#count[1]#">
<cfoutput>
From cfc (##count[1].recordCount##): #count[1].recordCount#<br>
From stored proc (##profile_head.recordCount##): #profile_head.recordCount#
</cfoutput>
I get:
From cfc (#count[1].recordCount#): 1
From stored proc (#profile_head.recordCount#): 1
But the second way looks so much cleaner.
-----------------------------WORKING SOLUTION------------------------------
So after working with the answer from #leigh, I came up with this.
Here is the full cfc:
<cfcomponent displayname="Fund Profile" hint="This is the cfc that will do the processing of all fund profile information" output="false">
<cfproperty name = "result1"> <!--- PROFILE HEAD --->
<cfproperty name = "result2"> <!--- ATTRIBUTION --->
<cfproperty name = "result3"> <!--- CHARACTERISTICS --->
<cfproperty name = "result4"> <!--- EXPOSURE --->
<cfproperty name = "result5"> <!--- WEIGHTS --->
<cfproperty name = "result6"> <!--- HOLDINGS --->
<cffunction name="init"
displayname="init"
hint="This will initialize the object"
access="public"
output="false"
returnType="Any">
<cfargument name="dsn" type="string" required="true" />
<cfargument name="cusip" type="string" required="true" />
<cfargument name="report_date" type="date" required="true" />
<cfset variables.dsn = #arguments.dsn#>
<cfset variables.cusip = #arguments.cusip#>
<cfset variables.report_date = #arguments.report_date#>
<cfscript>
getProfiledata(cusip,report_date);
</cfscript>
<cfreturn this>
</cffunction>
<cffunction name="getProfileData"
access="private"
output="false"
returntype="void">
<cfargument name="cusip" type="string" required="true">
<cfargument name="report_date" type="date" required="true">
<cfstoredproc datasource="#dsn#" procedure="prc_asset_profile_retrieve">
<!--- STORED PROCEDURE HASN'T CHANGED. SEE ABOVE FOR CODE --->
</cfstoredproc>
<cfscript>
setProfilehead(profile_head);
setAttribution(attribution);
setCharacteristics(characteristics);
setExposure(exposure);
setWeights(weights);
setHoldings(holdings);
</cfscript>
<cfreturn>
</cffunction>
<!--- NOT GOING TO INCLUDE ALL SETTERS AND GETTERS, --->
<!--- BECAUSE THEY ARE ALL THE SAME OTHER THAN THE NAMES --->
<cffunction name="setProfileHead" access="private">
<cfargument name="ProfileHead">
<cfset variables.result1 = arguments.ProfileHead>
</cffunction>
<cffunction name="getProfileHead" access="public" returntype="query">
<cfreturn variables.result1>
</cffunction>
</cfcomponent>
Here is the code from the calling page:
<cfset fund_profile = CreateObject("component", "CFCs.fund_profile").init("#dsn#","#cusip#","#report_date#")>
<cfset profile_head = fund_profile.getProfileHead()>
Sorry for all the code, but I wanted to make the code available. So does anyone see any problems with what I came up with?
A function can only return a single value. If you wish to return multiple values, you will need to use some type of complex object (an array, structure, ...) If arrays are not intuitive enough, you could place the queries in a structure and return that instead. Then the calling page could access the queries by name, rather than index.
(Side note, be sure to properly var scope/localize all function variables.)
<cfset var data = {}>
...
<!--- store query results in structure --->
<cfset data.profile_head = profile_head>
<cfset data.attribution = attribution>
...
<cfset data.holdings = holdings>
<!--- return structure --->
<cfreturn data>
I would create other methods in the CFC that would each be responsible for returning a result from the stored proc. In the main method , call setters
setProfileHead(profilehead:profileHead)
<cffunction name=ProfileHead>
<cfarguments name=ProfileHead />
<cfset variables.profilehead = arguments.profilehead>
</cffunction>
Then...
<cffunction name=GetProfileHead>
<cfreturn variables.profileHead />
</cffuction>