auto_complete Plugin and Virtual Attributes

The way you normally specify which fields in your form should auto complete, is to add a declaration within your controller that looks like:

auto_complete_for :model_name, :field_name

However, this only works for actual fields that exist in your database, not for virtual attributes. So :field_name cannot be a virtual attribute, but should be a real existing database field.

The solution is to create a virtual attribute in your model, that’s a getter method.

Let’s say you want your users to lookup make and models of Cars that are in your database by entering text in your form’s text field and you want this one field to auto complete. Assume, however, that your database stores the make and model as separate fields. You decide to define a virtual attribute called ‘make_model’ in your Car model.

* At this time, add some Car records in your database, if you don’t already have any, so you can actually test your work with the auto_complete plugin!

Let’s say you have a form in your erb with a text field called ‘make_model’ which maps to a virtual attribute called ‘make_model’ which will look like this:

def make_model
make + ', ' + model if self
end

Your controller may look something like this:

class CarsController < ApplicationController
  
  # used by index.js.erb by auto_complete_result (autocomplete plugin)
  # there's three cases, if nothing is entered in the get request, the entire result is returned
  # If what the user enters contains a comma, assume make, model was typed in, otherwise, it could be
  # make or model.
  # The first letter of the make and model must be entered for lookup to happen. 
  # This increases performance. Notice % is missing
  # from before #{make} and #{model}
  def index
    make_model_typed_in = params[:search]
    if make_model_type_in.blank?
      @cars = Cars.find(:all)
    elsif make_model_typed_in.include?(',')
      make_model_array = make_model_typed_in.split(',')
      make = make_model_array[0].strip
      model = make_model_array[1].strip
      @cars = Page.find(:all, :conditions => ['make LIKE ? and model LIKE ?', "#{make}%", "#{model}%"], :order => 'make, model')
    else
      make = model = make_model_typed_in.strip
      @cars = Page.find(:all, :conditions => ['make LIKE ? or model LIKE ?', "#{make}%", "#{model}%"], :order => 'make, model')
    end
  end
end

Now make the index action above be called via Javascript.
Create an erb file called index.js.erb under views/cars/. It should contain the following:

<%= auto_complete_result @cars, :make_model %>

Insert ‘debugger’ method call in the actual method auto_complete_result definition to see what’s passed into it. Edit the vendor/plugins/auto_complete/lib/auto_complete_macros/helper.rb file. Here I assume you already have the ruby-debug gem properly installed. If not, install it now, make sure it works with your environment and learn how to use it.

It should look like this:

  def auto_complete_result(entries, field, phrase = nil)
    return unless entries
    debugger
    items = entries.map { |entry| content_tag("li", phrase ? highlight(entry[field], phrase) : h(entry[field])) }
    content_tag("ul", items.uniq)
  end

Hit http://localhost:3000/cars.js and you should enter the (rdb:1) prompt.

The following returns nil which indicates that method auto_complete_result doesn’t know how to handle virtual attributes.

(rdb:1) p entries[0][:make_model]
nil

However, you won’t get a nil returned if you access a ‘real’ field that actually exists in your database.

(rdb:1) p entries[0][:make]
BMW

So, the solution that worked for me (from auto_complete_result field rather than method) was to replace "field" with "method" in the method’s definition line and replace "entry[field]" to "entry.send(method)" in the method auto_complete_result within the auto_complete plugin’s code.

So your method should look like this:

  def auto_complete_result(entries, method, phrase = nil)
    return unless entries
    debugger
    items = entries.map { |entry| content_tag("li", phrase ? highlight(entry.send(method), phrase) : h(entry.send(method))) }
    content_tag("ul", items.uniq)
  end

Now if you hit http://localhost:3000/cars.js you should get all of your make_model returned in <LI> tags inside a <UL> tag.

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)
Facebook Twitter Email

2 Comments to “auto_complete Plugin and Virtual Attributes”

  1. navid 3 February 2012 at 10:43 am #

    I now actually suggest using Autocomplete which is part of JQuery http://jqueryui.com/demos/autocomplete/

    VN:F [1.9.22_1171]
    Rating: 0.0/5 (0 votes cast)
    VN:F [1.9.22_1171]
    Rating: 0 (from 0 votes)
  2. Frank 1 April 2011 at 8:51 am #

    This works great! Thank you!

    VA:F [1.9.22_1171]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.22_1171]
    Rating: 0 (from 0 votes)

Leave a Reply