Marko Anastasov wrote this on July 25, 2009

Hacking Rails auto_complete plugin for Prototype

The official Rails plugin for adding auto-complete functionality to your forms which works with Prototype and script.aculo.us is a fair choice which suits most cases. Plus you can always easily write your own auto_complete_for_#{object}_#{attribute} action in the controller to replace the default query. Note that you should probably always consider doing it - by default, the generated query will do a SELECT * ALL from the given table, while a DISTINCT call might suffice:

  def auto_complete_for_post_title
    input = params[post][:title].split(",").last.strip
    posts = Post.all(
      :select => "DISTINCT(title)",
      :conditions => ["LOWER(title) LIKE ?", "%#{input}%"],
      :limit => 10
    )
    resp = "<ul>"
    posts.each { |p| resp << "<li>#{p.title}</li>" }
    resp << "</ul>"
    render :text => resp
  end

Here we will show how to auto-complete for comma separated input. The plugin as it is will generate Javascript code which will always replace any previous value in the input field, while it is often preferred to add things up in some way (eg when entering tags).

Our fork of auto_complete adds an option to pass a custom Javascript function to be called after user selects one of the items from the suggestion list (updateElement for Ajax.Autocompleter, it might have not been available at the time the plugin was written).

<%= text_field_with_auto_complete :post, :title, {}, { :update_element => "function(el) { autoCompleteUpdate(el, 'post_title'); }" } %>

Here’s how autoCompleteUpdate can look like. We want it to keep all previous entries and replace the partial input which triggered the auto-completion with the selected item:

function autoCompleteUpdate(selectedLi, fieldId) {
  var values = $(fieldId).value.split(',');
  var oldVal = '';

  for (i = 0; i < values.length - 1; ++i) {
    oldVal += ', ' + stripWhitespace(values[i]);
  }

  $(fieldId).value = oldVal + ', ' + $(selectedLi).innerHTML;
  $(fieldId).value = $(fieldId).value.substring(2);
}
comments powered by Disqus

About Marko Anastasov

Marko co-founded Rendered Text. He’s a programmer with a passion of creating something for other people, coupled with interests in how things work in the society and nature. He is having a great time working across many areas in the company, including product design, helping the engineering teams, empowering users and marketing. He likes to counterbalance it all with running, cycling or a day in the woods.

Suggested Reads

Inject is a fundamental building block

Inject is one of the fundamental, and most versatile constructs available in functional languages. It can be used to implement map, select, max, all? and a bunch of other iteration related methods. Unfortunately, many programmers are not aware of its awesome powers. This article is here to improve this fact.

Contact

Rendered Text is a software company. For questions regarding Semaphore, please visit semaphoreci.com. Otherwise, feel free to get in touch any time by sending us an email.

Rendered Text
Svetozara Miletica 10
21000 Novi Sad
Serbia