Coldfusion Wrapper for jquery Autocomplete

For one of my project I required autocomplete box. I know coldfusion has its own autocomplete but has lots of limitation. I wanted autocomplete to work like select box of html, display lable of product in list but in backend it will return ID for that product and as usual I just stop at jQuery-ui Autocomplete (I just love jquery) which allow good customization.

This coldfusion wrapper allow you to use jquery autocomplete with coldfusion query variables or loading data remotely.

Example:

[code:cf]
<cf_autocomplete
textFieldName="productName" textFieldValue="" textFieldBind="PRODUCTNAME"
idFieldName="productId" idFieldBind = "PRODUCTID"
datasource="/cfc/products.cfc?method=getActiveProduct&returnFormat=JSON"
fieldList="PRODUCTID,PRODUCTNAME,PRICE"
queryParam="product"
displayTemplate="<div><img src=""/images/{PRODUCTID}.gif"">{PRODUCTNAME}&nbsp;: {PRICE}</div>"
minlength=2 />
[/code]
Attributes:

Attribute Required/Optional Default Description
dataSource Required   Can contain two type of value
1. URL: Should return query in JSON format
2. Query: Query variables
delay Optional 300 The delay in milliseconds the Autocomplete waits after a keystroke to activate itself
delay Optional 300 The delay in milliseconds the Autocomplete waits after a keystroke to activate itself
disabled Optional false Disable autocomplete
displayTemplate Required   Custom display Template
Ex. <div>{ARTID}-{ARTNAME}</div> word srounded in curly braces will replaced with query field value.
fieldList Required   Field list of query. Must be in same order as writtern in query
idFieldName Optional   Specify hidden field name you like to create.
Optional, it will create hidden variable with specified value. Usefull to store ID.
idFieldBind Required if idFieldName avaialble   Query field bind with hidden element
idFieldValue Optional   Default value of hidden field.
minLength Optional   The minimum number of characters a user has to type before the Autocomplete activates. Zero is useful for local data with just a few items. Should be increased when there are a lot of items, where a single character would match a few thousand items.
queryParam Optional query URL parameter name passed for remote call.
randNo Optional Random number generated by system Append randno to id field to avoid conflicts.
textFieldName Required   Name of text field
textFieldBind Required   Value to be bound with text field. This field value will display in text field once value selected.
textFieldValue Required   Defult text field value.
autocompleteSearch Optional   Before a request (source-option) is started, after minLength and delay are met. Can be canceled (return false), then no request will be started and no items suggested.
autocompleteOpen Optional   Bind javascript function with autocomplete Triggered when the suggestion menu is opened.
autocompleteFocus Optional   Bind javascript function with autocomplete Before focus is moved to an item (not selecting), ui.item refers to the focused item. The default action of focus is to replace the text field’s value with the value of the focused item, though only if the focus event was triggered by a keyboard interaction. Canceling this event prevents the value from being updated, but does not prevent the menu item from being focused.
autocompleteSelect Optional   Triggered when an item is selected from the menu; ui.item refers to the selected item. The default action of select is to replace the text field’s value with the value of the selected item. Canceling this event prevents the value from being updated, but does not prevent the menu from closing.
autocompleteClose Optional   When the list is hidden – doesn’t have to occur together with a change.
autocompleteChange Optional   After an item was selected; ui.item refers to the selected item. Always triggered after the close event.

AutoComplete Code:

[code:cf]
<!—
Description: ColdFusion wrapper for jQuery autocomplete.
Requirement: jQuery 1.4+, jQuery UI 1.8+, ColdFusion 8.0+
Developer : Pritesh Patel (iSummation Technology Pvt. Ltd) https://www.isummation.com
Version : 1.0
License:
Licensed under the Creative Commons License, Version 2.5 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://creativecommons.org/licenses/by-sa/2.5/in/

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
—>
<cfoutput>
<!—Required:
Can contain two type of value
1. URL: Should return query in JSON format
2. Query: Query variables
—>
<cfparam name="attributes.dataSource" default="">
<!— Optional:
The delay in milliseconds the Autocomplete waits after a keystroke to activate itself
—>
<cfparam name="attributes.delay" default="300">
<!— Optional:
Disable autocomplete
—>
<cfparam name="attributes.disabled" default="">
<!— Required:
Custom display Template
Ex. <div>{ARTID}-{ARTNAME}</div> word srounded in curly braces will replaced with query field value.
—>
<cfparam name="attributes.displayTemplate" default="">
<!— Required:
Field list of query. Must be in same order as writtern in query
—>
<cfparam name="attributes.fieldList" default="">
<!— Optional:
Specify hidden field name you like to create.
Optional, it will create hidden variable with specified value. Usefull to store ID.
—>
<cfparam name="attributes.idFieldName" default="">
<!— Required if idFieldName avaialble
Query field bind with hidden element
—>
<cfparam name="attributes.idFieldBind" default="">
<!— Optional:
Default value of hidden field.
—>
<cfparam name="attributes.idFieldValue" default="">
<!— Optional:
The minimum number of characters a user has to type before the Autocomplete activates.
Zero is useful for local data with just a few items.
Should be increased when there are a lot of items,
where a single character would match a few thousand items.
—>
<cfparam name="attributes.minLength" default="0">
<!— Optional:
User typed text as URL varible to filter query
—>
<cfparam name="attributes.queryParam" default="query">
<!— Optional:
Append randno to id field to avoid conflicts.
—>
<cfparam name="attributes.randNo" default="#randRange(1000,100000000)#">
<!— Required:
Name of text field
—>
<cfparam name="attributes.textFieldName" default="">
<!— Required:
Value to be bound with text field. This field value will display in text field once
value selected.
—>
<cfparam name="attributes.textFieldBind" default="">
<!— Optional:
Defult text field value.
—>
<cfparam name="attributes.textFieldValue" default="">

<!— Autocomplete event binding —>
<!— Optional:
Before a request (source-option) is started, after minLength and delay are met.
Can be canceled (return false), then no request will be started and no items suggested.
—>
<cfparam name="attributes.autocompleteSearch" default="">
<!— Optional:
Bind javascript function with autocomplete
Triggered when the suggestion menu is opened.
—>
<cfparam name="attributes.autocompleteOpen" default="">
<!— Optional:
Bind javascript function with autocomplete
Before focus is moved to an item (not selecting), ui.item refers to the focused item.
The default action of focus is to replace the text field’s value with the value of the focused
item, though only if the focus event was triggered by a keyboard interaction.
Canceling this event prevents the value from being updated, but does not prevent the menu item
from being focused.
—>
<cfparam name="attributes.autocompleteFocus" default="">
<!— Optional:
Triggered when an item is selected from the menu; ui.item refers to the selected item.
The default action of select is to replace the text field’s value with the value of
the selected item. Canceling this event prevents the value from being updated, but does not
prevent the menu from closing.
—>
<cfparam name="attributes.autocompleteSelect" default="">
<!— Optional:
When the list is hidden – doesn’t have to occur together with a change.
—>
<cfparam name="attributes.autocompleteClose" default="">
<!— Optional:
After an item was selected; ui.item refers to the selected item. Always triggered after the
close event.
—>
<cfparam name="attributes.autocompleteChange" default="">
<cfif thisTag.ExecutionMode is ‘start’>
<cfif not len(trim(attributes.displayTemplate))>
<cfthrow message="attribute displayTemplate is required.">
</cfif>
<cfif not len(trim(attributes.fieldList))>
<cfthrow message="attribute fieldList is required.">
</cfif>
<cfif len(trim(attributes.idFieldName))>
<cfif not len(trim(attributes.idFieldBind))>
<cfthrow message="idFieldBind is required when idFieldName specified.">
</cfif>
</cfif>
<cfif not len(trim(attributes.textFieldName))>
<cfthrow message="attribute textFieldName is required.">
</cfif>
<cfif not len(trim(attributes.textFieldBind))>
<cfthrow message="attribute textFieldBind is required.">
</cfif>

<cfsavecontent variable="headerVal">
<cfoutput>
<script type="text/javascript">
//<![cdata[
$(function(){
<cfif isQuery(attributes.dataSource)>
var data = #serializeJSON(attributes.dataSource)#;
var ds#attributes.randno# = $.map(data.DATA, function(item) {
return {
<cfset getTemplateVar = REMatchNoCase("\{[\s\S]*?\}",attributes.displayTemplate)>
<cfset attributes.displayTemplate = jsStringFormat(attributes.displayTemplate)>
<cfloop from="1" to="#arrayLen(getTemplateVar)#" index="index">
<cfset attributes.displayTemplate = replaceNoCase(attributes.displayTemplate,"#getTemplateVar[index]#","’+ item[#listFindNoCase(attributes.fieldList,replaceList(getTemplateVar[index],"{,}",","))-1#] +’","all")>
</cfloop>
label: ‘#attributes.displayTemplate#’,
value: item[#listFindNoCase(attributes.fieldList,attributes.textFieldBind)-1#]
<cfset count = 0>
<cfloop list="#attributes.fieldList#" index="lc">
,#lc#:item[#count#]
<cfset count = count+1>
</cfloop>
}});
</cfif>
$("###attributes.textFieldName#_#attributes.randNo#").autocomplete({
<cfscript>
if(len(attributes.disabled) and attributes.disabled)
writeOutput("disabled:true,");
if(len(attributes.delay) and attributes.delay)
writeOutput("delay:#val(attributes.delay)#,");
writeOutput("minLength:#val(attributes.minLength)#,");
if(len(attributes.idFieldName))
{
if(len(attributes.autocompleteselect))
writeOutput("select: function(event, ui) {ui.item ? $(""###attributes.idFieldName#_#attributes.randNo#"").val(ui.item.#ucase(attributes.idFieldBind)#) : $(""###attributes.idFieldName#_#attributes.randNo#"").val(”);#attributes.autocompleteselect#(event,ui)},");
else
writeOutput("select: function(event, ui) {ui.item ? $(""###attributes.idFieldName#_#attributes.randNo#"").val(ui.item.#ucase(attributes.idFieldBind)#) : $(""###attributes.idFieldName#_#attributes.randNo#"").val(”);},");
}
else if(len(attributes.autocompleteselect))
writeOutput("select:#attributes.autocompleteselect#,");
if(len(attributes.autocompletesearch))
writeOutput("search:#attributes.autocompletesearch#,");
if(len(attributes.autocompleteopen))
writeOutput("open:#attributes.autocompleteopen#,");
if(len(attributes.autocompletefocus))
writeOutput("focus:#attributes.autocompletefocus#,");
if(len(attributes.autocompleteclose))
writeOutput("close:#attributes.autocompleteclose#,");
if(len(attributes.autocompletechange))
writeOutput("change:#attributes.autocompletechange#,");
</cfscript>
<cfif isQuery(attributes.dataSource)>
source:ds#attributes.randno#
<cfelseif IsSimpleValue(attributes.dataSource)>
source: function(request,response){
$.ajax({
url: "#attributes.dataSource#",
dataType: "json",
data: {
#attributes.queryParam#: request.term
},
success: function(data) {
response($.map(data.DATA, function(item) {
return {
<cfset getTemplateVar = REMatchNoCase("\{[\s\S]*?\}",attributes.displayTemplate)>
<cfset attributes.displayTemplate = jsStringFormat(attributes.displayTemplate)>
<cfloop from="1" to="#arrayLen(getTemplateVar)#" index="index">
<cfset attributes.displayTemplate = replaceNoCase(attributes.displayTemplate,"#getTemplateVar[index]#","’+ item[#listFindNoCase(attributes.fieldList,replaceList(getTemplateVar[index],"{,}",","))-1#] +’","all")>
</cfloop>
label: ‘#attributes.displayTemplate#’,
value: item[#listFindNoCase(attributes.fieldList,attributes.textFieldBind)-1#]
<cfset count = 0>
<cfloop list="#attributes.fieldList#" index="lc">
,#lc#:item[#count#]
<cfset count = count+1>
</cfloop>
}
}))
}
})}
<cfelse>
<cfthrow message="Invalid attribute value">
</cfif>
});
});
/*]]>*/
</script>
</cfoutput>
</cfsavecontent>
<cfhtmlhead text="#headerVal#">
<input type="text" name="#attributes.textFieldName#" id="#attributes.textFieldName#_#attributes.randNo#" value="#attributes.textFieldValue#" />
<cfif len(attributes.idFieldName)>
<input type="hidden" name="#attributes.idFieldName#" id="#attributes.idFieldName#_#attributes.randNo#" value="#attributes.idFieldValue#" />
</cfif>
</cfif>
</cfoutput>
[/code]