typeahead.js example (remote datasource with Handlebars template)

typeahead.js is an autocomplete utility written in javascript. typeahead is a little more intricate than, say, jQuery's autocomplete, but with the reward of greater customization.

Let's get started. First download typeahead here.

Since this plugin is dependent on the Bootstrap framework and jQuery first download these here and here. (Please note: typehead requires a jQuery version greater than 1.9).

Lastly, you'll need to include Handlebars for binding search data to an HTML template.

 <input id="custom-templates" type="text" class="form-control input-sm" placeholder="Search..." name="query">

I'm also including a server-side Groovy example. Ultimately, you just need to return an array of objects. Object property names can also be named anything - these are properly initialised for use in the Javascript example below.

def searchForChannel() {  
    def channels = []

    EngageClient.withCriteria {
      or {
        ilike('clientName', '%' + params.query + '%')
      }
    }.each { client -> channels += [label: client.clientName, value: client.id, type: 'client', products: client.products.size()] }

    EngageProduct.withCriteria {
      or {
        ilike('name', '%' + params.query + '%')
      }
    }.each { project -> channels += [label: project.name, value: project.id, type: 'project', products: project.dependOnProducts.size()] }

    render channels as JSON
  }
var channels = new Bloodhound({  
    datumTokenizer: function (datum) {
        return Bloodhound.tokenizers.whitespace(datum.value);
    },
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    remote: {
        url: '/dynamo/searchForChannel?query=%QUERY',
        replace: function () {
            var q = '/dynamo/searchForChannel?query=%QUERY';
            if ($('#custom-templates').val()) {
                q += "&query=" + encodeURIComponent($('#custom-templates').val());
            }
            return "/dynamo/searchForChannel" + q;
        },
        filter: function (channels) {
            // Map the remote source JSON array to a JavaScript object array
            return $.map(channels, function (channel) {
                return {
                    value: channel.value,
                    label: channel.label,
                    type: channel.type,
                    products: channel.products
                };
            });
        }
    }
});

channels.initialize();

$('#custom-templates').typeahead(null, {
    name: 'channel',
    display: 'label',
    source: channels.ttAdapter(),
    templates: {
        empty: [
            '',
            'Nothing found...yet',
            ''
        ].join('\n'),
        suggestion: Handlebars.compile('<div class="typeAheadContainer">' +
        '<div class="typeAheadIcon">' +
        '{{#ifCond type "project"}}' +
        '<i class="fa fa-paper-plane-o"></i>' +
        ' {{/ifCond}}' +
        '{{#ifCond type "client"}}' +
        '<i class="fa fa-diamond"></i>' +
        ' {{/ifCond}}' +
        '</div>' +
        '<div class="typeAheadName">' +
        '{{label}}' +
        '<div class="typeAheadType">{{type}}</div>' +
        '<span class="typeAheadProductCount"><i class="fa fa-shopping-cart"></i> {{products}}</span>' +
        '</div>' +
        '</div>')
    }
});

A few notes:

Bloodhound is used as an adapter for the autocomplete functionality. It essentially handles all queries by filtering over a result set returned from the server. Note: you need to declare all values you wish to use in the filter method.

Handlebars is used to bind data from the resulting JSON objects to an HTML template. I've included a very rough, strung-together example of its usage. You could probably substitute the stringified version with a proper template.