ColdFusion autosuggest in text field is not responsive - coldfusion

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})"

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"

using autocomplete with commas in a field

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>

Newbie, very basic CRUD via Application.cfc

So I've been using in-line (correct term?) ColdFusion code to run all my pages and have gotten to a point where I think I have a decent understanding of the basics, and want to take the next step. After a lot of cross-referencing, research, and trial and error, I've come up with the following 4 pages of which the intent is to be able to enter a username and password in the form page (crud.cfm), then, after submission, redirect the user to a page which displays the newly entered data, as well as any past entries.
I can do all of this with simple in-line code and an Application.CFM but I want to migrate toward a more OOP/Modular approach going forward, as presently I find myself rewriting/copy-pasting scads of code across several different pages. The error that I get when I submit from 'crud.cfm' is:
"The component attribute in cfinvoke tag has invalid value."
I have tried it without hashes and without the uppercase "A" to no avail. Here is my non-working code:
Application.cfc
<cfcomponent output="false">
<cffunction name="insertrecord" access="public" output="false">
<cfargument name="data" type="struct" required="true">
<cfquery name="create" datasource="test">
INSERT INTO logins(
username,
password)
VALUES(
'trim(form.username)',
'trim(form.password)')
</cfquery>
</cffunction>
</cfcomponent>
crud.cfm
<h3> Enter new user/password </h3>
<cfform name="thisform" method="post" action="procpage.cfm">
Username:<cfinput type="text" name="username" value="">
Password:<cfinput type="password" name="password" value="">
<input type="submit" value="submit">
</cfform>
procpage.cfm
<cfif !StructIsEmpty(form)>
<cfinvoke component="#Application#" method="insertrecord">
<cfinvokeargument name="data" value="#form#">
</cfinvoke>
</cfif>
<cflocation url="resultpage.cfm" addtoken="no">
resultpage.cfm
<cfquery name="read" datasource="test">
SELECT * FROM logins
</cfquery>
<table>
<tr>
<th>LOGIN</th>
<th>USERNAME</th>
<th>PASSWORD</th>
</tr>
<cfloop query="read">
<tr>
<td>#read.login#</td>
<td>#read.username#</td>
<td>#read.password#</td>
</tr>
</cfloop>
</table>
ColdFusion version 8, MSSQL 2005 database.
Thank you all in advance for your help, looking forward to your responses!
Application.cfc is a special file in ColdFusion.
http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=AppEvents_01.html
You'll need to name your component something else. Application.cfc is where you put Application events, setup code, etc.
In answer to your question to Miguel, Yes. The application.cfc is called each time a cfm is called. So when you hit the crud.cfm, application.cfc runs. When you hit procpage.cfm application.cfc runs etc..
so you want your procpage.cfm to look more like
<cfif !StructIsEmpty(form)>
<cfinvoke component="functions" method="insertrecord">
<cfinvokeargument name="data" value="#form#">
</cfinvoke>
</cfif>
and your functions.cfc looks more like
<cfcomponent output="false">
<cffunction name="insertrecord" access="public" output="false" returntype="void">
<cfargument name="data" type="struct" required="true">
<cfquery name="create" datasource="test">
INSERT INTO logins(
username,
password)
VALUES(
<cfqueryparam cfsqltype="cf_sql_varchar" value="#trim(form.username)#">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#trim(form.password)#">
)
</cfquery>
</cffunction>
<cffunction name="readRecord" access="public" returntype="query">
<cfargument name="loginID" type="numeric" required="false" default="0">
<cfquery name="read" datasource="test">
SELECT * FROM logins
where loginID = <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.loginID#">
</cfquery>
<cfreturn read/>
</cffunction>
</cfcomponent>
resultsPage.cfm
<cfscript>
f = createObject('component','functions');
r= f.readRecord(theIdToPass);
</cfscript>
<table>
<tr>
<th>LOGIN</th>
<th>USERNAME</th>
<th>PASSWORD</th>
</tr>
<cfloop query="r">
<tr>
<td>#r.login#</td>
<td>#r.username#</td>
<td>#r.password#</td>
</tr>
</cfloop>
</table>
You will find that cfqueryparam not only has a small performance boost but will protect you from sql attacks and such, USE IT!! Always.
You also might think about doing arguments for each one expect instead of using struct variables, since writting error trapping for a struct without know what you are expecting to be passed can be pretty frustrating.
Hopefully this can get you off to a positive start with CF!

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>