Ajaxariffic Autocomplete with Scriptaculous
Autocompletin' in Rails
With the magic of Prototype.js and Scriptaculous, the autocomplete is, in theory, extremely easy. But throw a lack of good documentation into the mix and something easy and "magical" and Bad Things Happen.
First Things First
One, check out the autocomplete demo. It's one of the more "magical" Ajax goodies because it automatically handles the appearance of the floating div, the keyboard navigation, the ability to click your choice with the mouse or use Tab or Enter to complete your entry.
Is that what you want? If not, you'll need to find another solution, because this stuff's all built in—you can't do an autocomplete with this code and get it any other way (although you can style it differently, to be sure). The pixie dust is non-negotiable.
Secondly, you'll need to get the very latest version (download link) of the Scriptaculous script files and also their patched version of Prototype.js. If you're using older versions of either software and having problems, this may be the reason.
The Simplest Autocomplete
You can almost scaffold autocompletion, as you can see in this very simple demo (also an official Scriptaculous demo). If you don't want to do anything fancy, this may be the way to go. At least you can see near-instant results this way.
The source for this is:
# view
<%= text_field_with_auto_complete :contact, :name %># controller
auto_complete_for :contact, :name
In the view, you're using the built-in Rails helper text_field_with_auto_complete, which takes two arguments: a symbol for the model and a symbol for the method (column) of the model you want to autocomplete on.
Other examples of this helper might be:
<%= text_field_with_auto_complete :book, :title %><%= text_field_with_auto_complete :user, :username %><%= text_field_with_auto_complete :user, :email_address %>
This not only does some Ajax magic, sending an Ajax request to your controller, but it creates a field appropriate for Rails use when creating or editing an instance of a given model, just like a normal form helper.
In the controller, you're using the method auto_complete_for which, surprisingly, takes exactly the same arguments as the widget in the view. Hey, that's not all that tough at all.
It probably goes without saying that you'll need to be working off a database model with actual, you know, content in it for this to work. But I guess it really doesn't go without saying since I just said it. Oops. There goes that idea.
Mr. Ajax Fancypants
Of course, once you taste the hors d'oeuvres (can I buy a vowel? what do you mean, we're out?), your tummy starts grumbling for dinner. Man cannot live on miniature quiches alone.
There are just a few rules you need to know about the Rails magic in order to create your own fancypants autocompletes to inspire those oohs and ahhs.
The View
If you're wanting to provide autocompletion for a field that belongs to a model, for example in an add or edit screen, you'll want to use the same old helper in the view as you would for the dead-simple autocomplete:
# view
<%= text_field_with_auto_complete :cookie, :flavor %>
If you want to do something like a live search, see the heading "Roll Your Own" a little further down.
Be Controlling
When you don't wanna go straight to the model for the dish, you'll want to avoid the scaffolding and write your own method in the controller. There's some magic here, too, since Rails is going to expect a method called auto_complete_for_cookie_flavor—the last two items being the arguments you gave to text_field_with_auto_complete. Phew.
To take a nod (and some slightly modified code) from Scriptaculous, here's what you might do in your controller:
def auto_complete_for_cookie_flavor @cookies = Cookies.find(:all, :conditions => [ 'LOWER(flavor) LIKE ?', '%' + params[:cookie][:flavor].downcase + '%' ], :order => 'name ASC', :limit => 8) render :partial => 'cookies' end
As you can see, auto_complete_for_cookie_flavor is a custom controller method which searches for cookies matching the flavor the user has been in the process of typing. This method will get called every time someone presses a key in the cookie flavor autocomplete field so the result will get ever more filtered. Sweet.
I'm Rather Partial to Lists
But we're not done yet... you need to have that cookies partial. And it has to be crafted a certain way, because the Javascript powering all the magic is very particular.
Here's what the view partial might look like:
# filename: _cookies.rhtml
<ul class="cookies">
<% for cookie in @cookies do -%>
<li class="cookie">
<div class="image">
<img src="/images/cookie/<%= cookie.id.to_s[-1,1] %>.jpg"/>
</div>
<div class="name"><%=h cookie.name %></div>
<div class="flavor">
<span class="informal"><%=h cookie.flavor %></span>
</div>
</li>
<% end -%>
</ul>
This is because the Javascript expects only a UL—an unordered list—and its contained list items (and you have to make sure to write good XHTML and close all your tags, including <li></li>). So anything fancy that you can do within the confines of the list item tags, you can probably get away with.
If you need more help, load up your site in FireFox, play with the autocomplete for a second or two, then open the DOM Inspect from the Tools menu. It'll show you the autocomplete div even if it's currently hidden, and you can find out the class names and ids of any contents.
Roll Your Own
If you want to work off a field that does not belong to a column in one of your models, or don't want the drop-down/keyboard nav/tab completion magic, you will want to use auto_complete_field instead, like thus:
<input type="text" name="snacks"> <div id="snacks_auto_complete"></div> <%= auto_complete_field 'snacks', :url => {:action => 'complete_me'} %>
The auto_complete_field method lets you specify your own controller and method to get the autocomplete results, and it automatically assumes you want the data to be populated in a div or other HTML element with the id that equals the name of the input field plus _auto_complete.
Remember that this won't provide all the fancy auto-magic with the drop-down div and select stuff. But it also means you can craft a response in any format you like, not just a UL.
Stylin'
Whether you use the fancy custom goodies above or the original easy-peasy scaffolding, you're never quite done til everything looks gorgeous and high-tech and Web2.0-worthy. But your list will look like—I'm just going to be blunt here—crap if you don't use the powers of CSS. You may not even be able to see your autocomplete results because they might not be floating on top of your content, as they're supposed to.
Here's some sample CSS that I, uh, borrowed from Scriptaculous' demo. This doesn't cover the above fancy stuff, but it will get you started, because anything you output will get wrapped in a div called autocomplete and there are other things you can count on, such as the list item that your user is currently on—whether via keyboard arrow keys or mouse movement—being given the class selected.
<style type="text/css">
div.auto_complete {
position:absolute;
width:250px;
background-color:white;
border:1px solid #888;
margin:0px;
padding:0px;
}
li.selected { background-color: #ffb; }
</style>












yet another great rails article...thanks a lot...it was del.icio.us :D
I've been ignoring Web 2.0 for WAY too long, and this is yet another example of what you SHOULD be doing instead of following some old, stuffy guidelines.
The autocomplete stuff is something that would probably put an interesting amount of load on your system (like DB queries to pull info out, for example) but it would make the user experience a lot better. In my day job, we've been converting over a country-state-city dropdown sequence to be AJAXy, and while the load hasn't dropped in terms of database stuff, it's less load to have pages constantly refreshed.
Besides, you can never have enough documentation on how to do ANYTHING remotely complicated.
Wonderful. And timely, for my next project. Thank you.
Where was this a few days ago when I was pouring over the scriptaculous docs??? Good stuff. I just need to figure out how to autocomplete based on a locally loaded JS array...
Awesome article :D
Ajax.net -- the best!
Hi there. Take a look on some interface clientside features of my sample autocomplete combotextbox:
http://www.pixel-apes.com/rubrika/combotextbox/
It is completely clientside though (because it deals with <select> and written for some Greasemonkeys plugins)
great tutorial !
You may want to double-check the links you've posted to Prototype and Script.aculo.us -- I downloaded the linked versions in this article and couldn't get the simplest example to work (display:none; on the results div never flipped), but a fresh overwrite from the 'rails' command (0.13.1) installed versions that worked happily with one another.
Thanks for sharing!
I tried to use this and with my normal browser, Safari, and it wasn't working. I thought I would try it with Firefox, since I could look in the JS console, but for some reason it works in FF. Is this a Safari 2.0.1 bug, or a RoR bug, or a Scriptaculous bug?
Nice article but there are some very strange peculiarities about the autocomplete functions which you forgot to mention (at least I get them and I'm using the latest version of rails, scriptaculous and prototype) and they all seem to be related to the partial you use to construct the seach list (_cookies.rhtml).
Please note that I haven't tried the exact example shown here... it had some minor modifications but I wouldn't attribute the peculiar behaviour of the autocomplete function to these changes.
First of all, if you indent the contents of <li></li> with spaces of tabs then you will also get some in your autocompleted text field after you select an entry. Things like: <pre> <li class="cookie"> <div class="name"><%=h cookie.name %></div></pre> will insert extra spaces in the text field. You should omit the spaces to get a correct output: <pre><li class="cookie"> <div class="name"><%=h cookie.name %></div></pre>
Another thing that's very strange is that you must include an "informal" class for the element you don't want to get in your text field. In the _cookie.rhtml example above, if you don't include that
span class="informal"you will also get the cookie.flavor value in your text field. I don't know about the URL since what I tried didn't include one but if you have more than one value in the <li></li> and you don't assign the "informal" class to them you will get all of them in your text field.I must admit that I found all of this very strange, so maybe I'm doing something wrong. This is indeed a possibility. However, I strugled almost half a day with this before figuring the above. Has anyone else had any similar experiences with this?
That didn't come out right. Let's try again:
First of all, if you indent the contents of <li></li> with spaces of tabs then you will also get some in your autocompleted text field after you select an entry. Things like:
<li class="cookie">
will insert extra spaces in the text field. You should omit the spaces to get a correct output:
<li class="cookie">
<div class="name"><%=h cookie.name %></div>
wfewfe
...taking off my heat and apload till there is a pain in my palms:)
The only thing, took me a litle of confusion where to put
autocompletefor :contact, :name
If you put it not right after the declaration of the controller but in the action, you will get a noMethodError.
Other than that, simply great.
Don't forget to include <%= javascriptincludetag :defaults %>
in your document.
To Alex,
Thanks so much for that quirks comment. I spent half the day trying to figure it out also. I had indents and it was only inserting the spaces and nothing else. I counted the spaces that were there and it was the same as an indent but I didn't think it would "really" do that, so I didn't test. Anyway thanks for this post, saved me more troubles :)
Another problem I had was that using the most recent scriptaculous files (v1.6) I could not get this tutorial to work. ?
Ok fixed my last problem. I was using prototype 1.4 rather than the 1.5.0_pre1 that's shipped with scriptaculous. Hmm.. quite silly.
I use Firefox in Ubuntu :)
Scriptaculous is awesome n u ppl make it easier by explainin it your way. gr8!!!
Scriptaculous is awesome n u ppl make it easier by explainin it your way. gr8!!!
That's awesome...
Well done, nice instructions!
Thats correct!
There is some strange behaviour with this.
I thout to do it in my local version :)
Well done, nice instructions :(
I think it would be usefull for other users also.
Good idea...
I'm working :(
I think it would be usefull for other users also!
I think it would be usefull for other users also.
Well done, nice instructions...
Try AutoAssist (http://capxous.com/autoassist/) for those who didn't use ror
But I'm not sure why...
Any idea on how to combine it with something like this: http://mudabone.com/aietc/?page_id=410
?
I have a select like this:
<%= collectionselect(:parent, :doctorid, @doctors, :id, :name) %>
that I want to replace with an autocomplete field. This is working:
<%= textfieldwithautocomplete :parent, :doctorid, {}, :skipstyle => true %>
BUT: The lookup field comes back with a string. How can I get an id in stead of this string?
Well at last catched the problem.
I had and the fix I found...
Thanks :(
Thats correct.
There is some strange behaviour with this :(
Thanks for the write-up :(
Not really new :)
i am not sure as to why :(
I had and the fix I found.
I use Firefox in Ubuntu.
The content of your show is great, I really enjoy it...
Thats correct :(
The problem is my browser :)
That is strange...
Thanks for the write-up :)
Well at last catched the problem.
The problem is my browser.
Good idea :(
I think it would be usefull for other users also...
That is strange.
I was very dissapointed of this!
Gonna have to give it a try :)
Thanks for taking the time to do it :(
Thanks for the write-up :)
The content of your show is great, I really enjoy it :)
I think it would be usefull for other users also...
The content of your show is great, I really enjoy it!
I think it would be usefull for other users also!
That is strange!
But I'm not sure why :(
I think it would be usefull for other users also...
That is strange.
i am not sure as to why :)
Thats correct :(
Thanks.
Thanks for taking the time to do it :(
i am not sure as to why :(
But I'm not sure why :)
I'm working...
I thout to do it in my local version :)
Well at last catched the problem :(
I was very dissapointed of this :)
I think it would be usefull for other users also :(
I thout to do it in my local version :)
That is strange...
The problem is my browser!
i am not sure as to why...
The content of your show is great, I really enjoy it :)
Not really new :(
to add a id in autocompletefield follow this tutorial, but add in view:
search the manufacture
---view---
<%= hiddenfield :product, :manufactureid%> <%= textfieldwithautocomplete :manufacture, :name%>
--Controler---
model :manufacture def autocompleteformanufacturename @manufactures = Manufacture.find_by_sql( "SELECT * FROM manufactures WHERE name LIKE '%"+params[:manufacture][:name].downcase + "%' ORDER BY name ASC LIMIT 10") render :partial => 'manuf' end
-- _manuf.rhtml --
<% for manufacture in @manufactures do -%>- <%=manufacture.name%>
<% end -%>
Ha..!!! if it does not forget:
in method ...def show... and def edit.. @product = Product.find(param[:id]) @manufacture = Manufacture.find(@product.manufacture_id)
this show the name of manufacture in auto text fiel...
:p
i am not sure as to why!
I thout to do it in my local version :(
but when it is wanted the id, the better choise is to use methods findor_createbyname(param[:namefield_autocomplete])
search the manufacture and return id:
view <= hiddenfield :product, :manufactureid> <= textfieldwithautocomplete :manufacture, :name>
Controler
model :manufacture
def autocompleteformanufacturename @manufactures = Manufacture.find_by_sql( “SELECT * FROM manufactures WHERE name LIKE ‘“+params:manufacture.downcase + “’ ORDER BY name ASC LIMIT 10�)
render :partial => ‘manuf’ end
_manuf.rhtml -
<%for manufacture in @manufactures do%>- <%=manufacture.name%>
<% end -%>
but when it is wanted the id, the better choise is to use methods findor_createbyname(param[:namefield_autocomplete])
Very clear :)
Well done, nice instructions :)
I had and the fix I found :(
I had and the fix I found...
Very clear.
That's awesome!
I was very dissapointed of this :)
I thout to do it in my local version...
But I'm not sure why!
I thout to do it in my local version :)
I thout to do it in my local version.
I'm working :)
I was very dissapointed of this...
I thout to do it in my local version :)
That is strange.
I use Firefox in Ubuntu...
Gonna have to give it a try!