Thursday, 3 April 2008

A better autosuggesting widget

Following on from my previous post about extending the cfinput auto-suggest functionality (a tidy autosuggesting solution), I decided that my solution was too narrow. I have now written a custom tag that allows access to all the useful properties of the auto-suggest widget.

The tag can be found @ http://betterautosuggest.riaforge.org/

Here is a quick and slim example of how it can be used:

<cfimport taglib="myCustomTagsFolder" prefix="custom">

<cfform action="" method="post">
<custom:betterautosuggest
name="fruit"
autosuggest="apple,banana,lemon,lime,mango,orange,peach,pear"
delimchar=";"/>
</cfform>



Enjoy :)

20 comments:

larksys said...

Can I assume that I can use a bind to a URL for data?

Dom said...

It works in the same way as cfinput autosuggest="{whatever}". So yes. There is a known issue with queryMatchContains and cfc binding at the moment though. Will post about it on the riaforge project shortly.

Dom

aaron said...

Hi Dom.... Any progress re it working with cfc binding? Cheers.

Dom said...

'It' works but the queryMatchContains does not which I realise is a real pain.

The problem is the way in which CF creates a new yui datasource for the widget when using a binding, overriding the settings set in the custom tag. I don't foresee any code within the custom tag addressing this issue but Mike Klostermeyer suggested this quick fix (which will work if you have only a single auto suggest):

<cfajaximport scriptSrc="CFIDE/scripts" cssSrc="CFIDE/scripts/ajax"
tags="cfinput-autosuggest">
<script>
try{
YAHOO.widget.DataSource.prototype.queryMatchContains = true;
} catch (hmm ){
alert( hmm );
}
</script>

If you're really needing to do this however, I'd suggest using the YUI widget directly. The YUI js library is really easy to use after you get started.

HTH

Dom

Tim said...

Great posts...

For those of us using unix be carefull of case when using the example. custom:betterAutoSuggest.. is not the same as custom:betterautosuggest...

Here's my error:The specified CFC userid could not be found

when using: autosuggest="cfc:userid.lookupuserid({cfautosuggestvalue})"

above cfc works finw without the betterAutoSuggest custom tag.

Any assistance is greatly appreciated. - Tim

Dom said...

Hi Tim, the cfc binding has nothing to do with the custom tag; that is all done by cfinput. If you do a search on cfautosuggest and Ben Forta you'll find a great post on this and issues that people have come accross.

Just a sanity check though - is your cfc really called 'userId' (as opposed to just 'user')?

Tim said...

Shouldn't autosuggest="{whatever}" work? I greatly appreciate your quick reply and any further assistance should you find the time.

This works:
<cfimport taglib="../betterautosuggest" prefix="custom">
<cfform>
Userid: <custom:betterAutoSuggest name="userid"
autosuggest="apple,plum,orange"
forceselection="true"
typeahead="true"
useShadow="false"/>
</cfform>

This works:
<cfform>
Userid: <cfinput type="text"
name="userid"

autosuggest="cfc:userid.lookupuserid({cfautosuggestvalue})">

</cfform>

This does not work:
<cfimport taglib="../betterautosuggest" prefix="custom">
<cfform>
Userid: <custom:betterAutoSuggest name="userid"

autosuggest="cfc:userid.lookupuserid({cfautosuggestvalue})"

forceselection="true"
typeahead="true"
useShadow="false"/>
</cfform>

Dom said...

Aha I see; it may be to do with the relativity of the path of the cfc to the calling template - which in this case is betterAutoSuggest.cfm. Perhaps you need to setup a mapping that you can use - or reference the cfc as if you were calling from the directory above (ie. "../").

HTH

Tim said...

Very interesting... the actual CF error

"The specified CFC userid could not be found.
The path to the CFC must be specified as a full path, or as a relative path from the current template, without the use of mappings."

An absolute & relative path to my cfc produces the above error. Not sure of the syntax needed but gave several attempts (ie: "../", "server.dir.dir.cfc", "expandpath function"...)

A mapping to the directory containing the cfc has no problems.
winner winner
autosuggest="cfc:mappedDirWithCFC.userid.lookupuserid({cfautosuggestvalue})"

(I assumed a mapping would not work as the absolute path did not work.)

Also, my earlier blog entry was not totally accurate. The entry with the fruit list is not 100% functional. The showloadingicon is not displayed while idle or loading.

Thanks again.

allen said...

I am trying to test it on our server (windows 2003) and I not able to get it to work. Do I need to install it using coldfusion admin. Please help.

allen said...

I think i am getting a javascript error that coldfusion is undefined. You help would be appreciated.

Dom said...

Hi allen, this is not to do with the custom tag but with CF8. Basically, you need to have /cfide/scripts available to your page (the js that cf8 produces sits in there). See this article:

http://www.coldfusionguy.com/ColdFusion/blog/index.cfm/2007/11/13/CF8-AJAX-Features-CFIDEScripts-and-CFAjaxImport

If this is not the issue, let me know.

Tim said...

Inerestingly I have been looking for a solution to the javascript warning 'ColdFusion is undefined' just yesterday.

The datefield cfinput does not function. This only seems to happen randomly on IE6, but a page refresh fixes the issue. Then the issue remains dorment until temp internet files are removed from the browser.

Generated tag by CF:
<script type="text/javascript">
ColdFusion.Ajax.importTag('CFINPUT-DATEFIELD');
</script>

I have a j2ee unix install so case matters, and here is my dir structure:\CFIDE\scripts\ajax\package

Please help me understand the mappings. In the above tag, 'ColdFusion' should map to CFIDE? CF mapping or server mapping? Then 'Ajax'??

Dom said...

The 'ColdFusion' is not a CF mapping and does not relate to CF in anyway - client side code cannot directly access CF afterall. It will be a javascript namespace created in a .js include (in cfide/scripts).

I believe I have seen this js error happening when other YUI javascript is being used in a page.

For this, and other reasone, I now just use the YUI library (and others) directly without using the cf ajax tags.

Dominic

allen said...

Thank you Dom. After reading the article and fixing the cdide mapping, it worked great. Thank your for the quick response and help.

Allen

Dale said...

A great widget. I notice that the dropdown list does not stay in the foreground (other text fields in the foreground of the list instead) using I.E. In Firefox the dropdown list properly is in the foreground.

Dom said...

Yes Dale, a known issue with the cfinput autosuggest tag. I believe there is a css workaround documented out there somewhere.

Matt said...

is there a way to increase the input box size? size="" does not work

Dom said...

The size attribute should get passed to the cfinput tag - can you see the size attribute in the produced markup?

Either way, I would suggest sizing the input with css.

Tony said...

Excellent custom tag you've built there. I was trying to extend the build in version but it wasn't working so I augmented yours instead. A couple of worthy notes that people seem to miss:

Any attribute you pass will be passed back to the input so style, onclick, onmouseover, etc will work.

In order to be able to toggle between matching a string within the result or at the beginning of the result, you need to pass queryMatchContains as true and then later you can set YAHOO.widget.DataSource.prototype.queryMatchContains to false without having to reload the page.

I added itemSelectEvent to the custom tag, which fires a function after clicking the item in the drop down list. A very useful function if after you select an item you want to run a JS function with the result. I was not able to get it working with the built in ColdFusion version.

For a full list of events, check out the Yahoo page: http://developer.yahoo.com/yui/docs/YAHOO.widget.AutoComplete.html

Thanks again. This saved me from having to rewrite the entire code in JS and figure out how to return wildcard results with YUI.