Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby on Rails - Simple Form autocomplete association search

I've a form in a basic task management app which allows Tasks to be assigned to Users (task belongs_to user). I'm using Simple Form for this.

Currently, the association is populated in the typical manner, with a dropdown list of users as such: <%= f.association :user, label_method: :name, value_method: :id, include_blank: false %>.

However, as the number of Users grows, I'm looking to change this to an autocomplete form field to find users. I've tried following the Railscast on the subject, though it doesn't use Simple Form and isn't working on my implementation.

Here's my form, model and .coffee code:

Form

<%= simple_form_for @task do |f| %>
  ...
  <%= f.input :user_name, as: :search, data: 
      {autocomplete_source: User.pluck(:name)} %>
  ...
  <%= f.button :submit %>
<% end %>

Model

# Virtual attribute for autocomplete form field
  def user_name
    user.try(:name)
  end

  def user_name=(name)
    self.user = User.find_by_name(name) if name.present?
  end

tasks.coffee

jQuery ->
  $('#task_user_name').autocomplete
    source: $('#task_user_name').data('autocomplete-source')

This generates the following HTML:

<input class="string search optional form-control 
ui-autocomplete-input ui-autocomplete-loading" type="search" 
value="Robert Strong" name="task[user_name]" id="task_user_name" 
autocomplete="off">

Though the following error in the dev tools console:

Uncaught TypeError: this.source is not a function

Concerned about the autocomplete="off" within the HTML and can't imagine the TypeError helps much!

I'm stumped, so would greatly appreciate any advice on getting this working! Thanks in advance, let me know what you think, Steve.

like image 527
SRack Avatar asked Apr 21 '15 17:04

SRack


1 Answers

autocomplete_source is expecting an array or a URL that when called returns JSON with the results that match the query. http://api.jqueryui.com/autocomplete/#option-source

Right now you have

  <%= f.input :user_name, as: :search, data: 
  {autocomplete_source: User.pluck(:name)} %>

Thats probably going to return a string for javascript. So you could change it to something like:

  <%= f.input :user_name, as: :search, data: 
  {autocomplete_source: User.pluck(:name).to_json} %>

And then parse that JSON when setting up your autocomplete:

jQuery ->
  $('#task_user_name').autocomplete
    source: JSON.parse($('#task_user_name').data('autocomplete-source'))

In the long run though (when you have a lot of users) this will impact the load time of your page significantly. You should really instead follow that railscast and put a URL as the autocomplete source:

  <%= f.input :user_name, as: :search, data: 
  {autocomplete_source: users_path} %>

Make sure to add the json render path to the index action on your users controller.

If your index action is used for other things too you can use respond_to:

# users_controller
def index
  @users = User.order(:name)
  @users = @users.where("name like ?", "%#{params[:term]}%") if params[:term]

  respond_to do |format|
    format.html  # index.html.erb
    format.json  { render :json => @users.map(&:name) }
  end
end
like image 147
Oliver Nicolaas Ponder Avatar answered Oct 02 '22 08:10

Oliver Nicolaas Ponder