17 9 / 2012

Hi guys!

Again it was long lull in my blog and now I ready to write small post.

In my current project I had a need to use autocomplete tool but with very specific query to db. Certanly, all you know about excellent gem https://github.com/crowdint/rails3-jquery-autocomplete But there is two ways in which you won’t be comfortable using it.

  1. When you (like me) want to use heavy and complicated query for search suggestions
  2. When you want simple solution and don’t want unnecessary features from gem 

When I tried to write it with my hands, I found that documentation of jQuery UI for autocomplete plugin is incomplete and it took a while to sort out and find “how it should work”. Therefore, I decided to write small manual to you, so now you can do it quick and easy.

So, lets start.

First thing you should do is of course to add jquery-ui to application.js. 

//= require jquery
//= require jquery_ujs
//= require jquery-ui

Next create controller for your autocomplete resource

class TagsController < ApplicationController
  def autocomplete_tag_name
  end
end

After that add route for your search action

  resources :tags, only: [] do
    get :autocomplete_tag_name, :on => :collection
  end

Now it’s time to go into frontend. In your view you should have text field, to which you apply autocomplete:

  = f.input :tag_list, input_html: {
    value: '', class: 'tags_with_autocomplete', 'data-autocompleteUrl' => autocomplete_tag_name_tags_path
  }

In that example I’m using https://github.com/plataformatec/simple_form to build form, so if you using another builder, just follow it’s syntax. Here we added input field with specified class and html5 attribute - data, where we placed our autocomplete action url.

What about js? Few words about it. I guess, all of you know that placing js code into application.js is a way to hell. Don’t do that. I prefer to create separate file (in my case it common.js.coffee) and connect it in application.js. So next code I’ll place into common.js.coffee:

$ ->
  $('.tags_with_autocomplete').autocomplete
    minLength: 2
    source: (request, response) ->
      $.ajax
        url: $('.tags_with_autocomplete').data('autocompleteurl')
        dataType: "json"
        data:
          name: request.term
        success: (data) ->
          response(data)

What do we do here? Lets see step by step. We have jquery function autocomplete, in which we pass necessary params (full list of params you can see on http://jqueryui.com/demos/autocomplete/). First we set minLength params. It specifies the minimum number of characters a user has to type before the autocomplete activates. I think, that 2 is good choise, but you can set it to 3. Next param is essential to us. It defines from where script should take suggestions for autocomplete. Certanly best choise is using remote url with JSON response. Source param takes two arguments - request and response. First - is object which you take from user’s input. Second - what you should return. So we make ajax request to our server on url specified in url data attribute of our input. In data you can use another param name, but I prefer to use name. Request object has attribute term, in which stores user’s string from input. We will give it into params to our server. And really important thing here is success function. It’s standart js function which activates when response from server returns with status 200 (OK). And here we should call special autocomplete function response with json data returned by server. Don’t forget that, it was little thing, that I didn’t write, so nothing worked. 

And last you should make is controller specific code. In my simplified case it looks like:

class TagsController < ApplicationController
  def autocomplete_tag_name
    tags = Tag.select([:name]).where("name LIKE ?", "%#{params[:name]}%")
    result = tags.collect do |t|
      { value: t.name }
    end
    render json: result
  end
end

Few words about that. First we select only name column, because I need search only on it, and it will give us good speed up. Next where clause with like statement. After that we need to form our result to needed format. Jquery autocomplete takes such hash {id: 1, value: ‘value’, title: ‘title’}. Field id is unnecessary, field title too (in that case it will use value instead of title), so I form it to {value: ‘value’} hash. That’s all.

Now you can use your own autocomplete system and use any complex queries and sorting in your action. Or you can get rid of the excess gem, by adding just one controller method and few js lines. 

Enjoy it!

  1. graffzon это опубликовал(а)