Autocomplete with HTML Results

Episode #83 by Teacher's Avatar David Kimura

Summary

Working with an autocomplete can be very simple. However, things get complicated when you try to use HTML markup in the displayed results. Learn how to use HTML markup within your autocomplete results!
rails view form javascript 7:02

Resources

Summary

# Gemfile
source 'https://rails-assets.org' do
  gem 'rails-assets-jquery'
  gem 'rails-assets-jquery-ui'
end

# application.js
//= require jquery
//= require jquery-ui

# application.css
 /*
 *= require jquery-ui
 *= require_tree .
 *= require_self
 */

.product .description {
  color: #b0bec5;
  font-size: 80%;
  font-weight: normal;
}

.ui-widget-content.ui-autocomplete{
  max-width: 350px;
}

input {
  height: 30px;
  width: 300px;
  padding-left: 15px;
}

# autocomplete.coffee
@autoComplete = ->
  $('.autocomplete').each ->
    $(this).autocomplete
      source: $(this).data('source')
      minLength: 2
    $(this).autocomplete('instance')._renderItem = (ul, item) ->
      $('<li>').append(item.label).appendTo ul

$(document).on('turbolinks:load', @autoComplete);

# app/services/autocomplete/application_autocomplete.rb
module Autocomplete
  class ApplicationAutocomplete
    delegate :params, to: :@view

    def initialize(view)
      @view = view
    end

    def as_json(options = {})
      [].tap do |column|
        results.each do |result|
          json = {}
          json[:label] = result_partial(result)
          json[:value] = result_value(result)
          column << json
        end
      end
    end

  end
end

# app/services/autocomplete/product.rb
module Autocomplete
  class Products < ApplicationAutocomplete

    private 

    def result_partial(product)
      ApplicationController.new.render_to_string(partial: 'products/autocomplete', 
                                                 locals: { product: product }).html_safe
    end

    def result_value(product)
      product.name
    end

    def results
      @results ||= Product.where('NAME LIKE :query', query: "%#{params[:term]}%").limit(5)
    end

  end
end

# routes.rb
Rails.application.routes.draw do
  resources :products do
    collection do
      get :autocomplete
    end
  end
  root to: 'welcome#index'
end

# welcome/index.html.erb
<%= text_field_tag :product, "", class: 'autocomplete', data: { source: autocomplete_products_path } %>

# products/_autocomplete.rb
<%= content_tag :div, class: 'product' do %>
  <%= product.name %>
  <% product.rating.times do |rate| %>
    <%= image_tag('star.png') %>
  <% end %>
  <%= content_tag :div, class: 'description' do %>
    <%= product.description %>
  <% end %>
<% end %>

# products_controller.rb
  def autocomplete
    render json: Autocomplete::Products.new(view_context)
  end