#102 Auto-Complete Association (revised)
A select menu is often used for setting a belongs_to association, but you should also consider using a text field with autocomple. Here I use jQuery UI and show two different solutions: client side and server side.
- Download:
- source code
- mp4
- m4v
- webm
- ogv
Wow. That's nice and clean. In this case though, it's a fairly simple example. Recently, I needed a hidden field with the id of the record, and a visible field with the name of the record (for a accepts_nested_attributes_for population). Custom JS and HTML I found was a bit more flexible.
I made a 'search' action on the Users controller, which implements the search and rendering of HTML, rather than JSON. I prefer this way because:
I prefer rendering HTML from an existing partial, than having to recreate that partial in Javascript and render it that way.
how did you get the id of the record in a hidden field? Thanks! I'm using devise and trying to assign an association between a User and an Organization upon registration.
Thank you for your casts, always appreciated!
Any advice about implementing it with a category description and category photo? Any comment is welcome!
Tried this with locations for a new event. It was working fine then I deleted a location and it keeps trying to download a json file instead of displaying the index. Any ideas?
Log:
Started GET "/locations" for 192.168.. at 2011-11-15 14:20:10 -0500
Processing by LocationsController#index as HTML
[1m[35mLocation Load (3.2ms)[0m SELECT "locations".* FROM "locations"
[1m[36mLocation Load (2.0ms)[0m [1mSELECT "locations".* FROM "locations" WHERE (name like '%%') ORDER BY name[0m
Completed 200 OK in 89ms (Views: 7.8ms | ActiveRecord: 8.4ms)
Thank!
See also :
http://harvesthq.github.com/chosen/
http://github.com/christianblais/searchify
good job!
i have one question.. method 'data:', is only in rails 3.1 ?
How would you incorporate this with a many to many association where you have a product and category and an association model with product_id and category_id?
Hi Dave, curious if you found a solution for doing this with a many-many association.
Thanks in advance, Cole
I too would like to see this with a many-to-many. I have the token-input solution working with one text field entry - but when I try to use multiples, it breaks. I would like to find a solution that has multiple fields doing this sort of thing. I'm sure this is a javacript/jquery deficiency on my part.
I'm also looking for a many-to-many solution that can handle more than one field. Has anyone managed this?
I keep getting:
Uncaught TypeError: Object # has no method 'autocomplete'
I've included jQuery UI in application.js:
//= require jquery-ui
... and my coffeescript:
jQuery ->
$('#location').autocomplete
source: ["foo", "food", "four"]
What am I missing!?
Indentation
yes - coffee script is whitespace sensitive
How would you do autocomplete and adding new items to the list without an association? like an open text field to add anything?
do you know if this works in rails 3.0.3?
How to implement with more than one word? I have problems when i use to autocomplete for example "java javascript" and load a table below in ajax. The value is "java" and not "java javascript".
I read that is a limitation of the plugin and I need other one, like bassistance. Anyone knows how to replace in rails with bassistance?
what if you have validations on the associated model?
in the model i have
and a geocoder like in episode #273 attached to spot.
when i have a spot_name that i cannot geocode i want the form to display the error but how do i do that?
Does anyone have any hints of how they could implement this on a has_many to has_many relationship, using a third modal to join them?
Are you looking for this?
http://railscasts.com/episodes/258-token-fields
The creation of the Category I need to pass the establishment_id how can I do this? being that he is not an attribute attr_accessible
I would just make it a nested resource if you could and grab it off the params array like params[:establishment_id]
So you would be on page like:
http://example.com/establishment/123/category/new
And that would post to
http://example.com/establishment/123/category
And you could pull off params[:establishment_id]
Hey Ryan, how could we validate these virtual attributes? I can validate without any problems the id that these _name setters refer to, but I can't output the feedback on the form. How could I do that? Thank you.
I think you need "%" like this if you try the last tutorial .
Thank you! I was going crazy.
Great video. I got a subscription just to see it.
One big question: How can I pass extraParams? I need to pass another id with the term and can't seem to get it to work.
With the "server side solution" you can just pass your extra params through the path helper like this and the autocomplete jquery plugin will append them to the Ajax request:
_form.html.erb
<%= f.text_field :category_name, data: {autocomplete_source: categories_path(your_extra_param: 'value')} %>
Let's say that your_extra_param should be the value of a second text field (you have categories and products and you want to autocomplete the products associated only with the selected category).
How can you make it work? Unfortunately I cannot figure it out.
Thanks.
Did you ever figure this out? Trying to do the same thing. Thanks!
Great screencast. It helped me to DRY this problem a little bit up for SIMPLE model constellations as mentioned in this screencast.
Enclosed you'll just find an untested refactoring version but an older similar refactoring version has been tested successfully ;-)
So I would generally transform all tags with a data-autocomplete-source attribute into an autocomplete input.
application.js.coffee
This autocomplete data source can be put in a separate generic autocomplete action instead of the index action and be introduced to the client through a view helper:
application_helper.erb
_form.html.erb
<%= autocomplete_input(f, :category) %>
routes.rb
This module about a generic autocomplete action can be included at the top of every resource controller based on a ActiveRecord::Base child class (you have to set the autocomplete route for each controller as shown above):
The association setters could be generated dynamically through meta programming.
So the following code generates special setter methods for all belongs_to and polymorphic associations when included in an ActiveRecord::Base child class.
P.S.: Maybe it turns out to be worth to extract in a gem some day ;-)
UPDATE: You have to wrap the following code around the columns loop to get it working with migrations:
How would one test this with rspec & capybara? I haven't been able to select an option from the autocomplete list with capybara. Anyone been successful with this?
Found this line, from here:
page.execute_script %Q{ $('.ui-menu-item a:contains("#{the_option_text_you_want_to_choose}")').trigger("mouseenter").trigger("click"); }
And it works!
If you don't need to be able to add new records to the referenced model implicitly then there's a nicer, pure jQuery option that works solely on IDs and does not require you to add accessors to your models: https://gist.github.com/3842296
We are trying to make the sample code working on post and user (instead of product and category) with user has_many posts and a post belongs_to a user. However there is a network error below. (even when source: was changed to source: ["choice1", "choice2"] and data: (autocomplete_search :@users.map(&:name)} in view file
"NetworkError: 404 Not Found - http://localhost:3000/ajax/users?term=cha"
Any idea about fixing the error?
Can someone explain how to get this working with mongoid?
many many thanks
Hi all. I encountered an issue where the CSS provided in this episode wasn't highlighting the selected search term.
I dug around and found out that for some reason JQueryUI changed the class of "active" search items from
a.ui-state-active
toa.ui-state-focus
. If you make that change to Ryan's CSS, the example works again.Charles is right! For more information, see the "Interaction States" section at http://docs.jquery.com/UI/Theming/API
If you want to read the jquery-ui code, you won't find the 'ui-state-*' classes in
jquery.ui.autocomplete.js
; you'll need to look injquery.ui.menu.js
.at 8:07.. How would I return more than just the name of the object?
I need to supply several other attributes as well although I'm having a hard time finding a simple explanation for using .map to return other attributes as well.
I have the following setup
Rails 3.2.12
when I type 'f', nothing gets populated. Any ideas?
I have javascript enabled on my browser.
Maybe it's a small miss,
but I thing this
$
should be passed to the func :Anyway, thanks you for your screencasts, Ryan Bates ! :)
Same problem here, nothing gets populated. Have you figured it out?
thx!
Proper indentation is often the solution to a coffeescript problem
Everything works up until I switch the source to anything non-local (source: ['foo', 'food', 'four'] works fine).
This is the error I'm seeing in the console while typing for non-local data:
Uncaught TypeError: Property 'source' of object [object Object] is not a function
Any suggestions? My code is almost identical to the screencast.
I have the same problem :(
Try this:
Got it working, great video! but how can I get the id of the record in a hidden field?
I have one question. if I want to have the autocomplete-source to return more than one attribute. how do i handle that?
Just a heads up for those in Rails 4, I got stuck because of 1 thing:
the "find_or_create_by" method has been deprecated in rails 4
so instead of this
try this
source: http://stackoverflow.com/a/3046645/4195073
Hope that helps someone!
Thank you sir!
Rails 4
This also works:
self.category = Category.find_or_create_by(name: name) if name.present?
This will also work.
This episode has been updated to Rails 5 as a blog post Auto-Complete Association in Rails 5
I have a problem in my project. I use this:
<%= text_field_tag :tag, nil, {"data-autocomplete-source": Tag.order(:name).map(&:name)} %>
and that outputs:
I need data-autocomplete-source to be array because of autocomplete. In rails console, Tag.order(:name).map(&:name) returns array. Thanks