March 2015

Volume 30 Number 3


Cutting Edge - Type Ahead

By Dino Esposito | March 2015

Dino EspositoSince the early days of the Web, most pages feature a search box to help you quickly find content within the page itself or on the site. A well-done search function is a must in larger sites. This helps users find what they want quickly and easily, bypassing the site map and architecture.

In a shopping site, for example, you might want to use query strings to look up products, offers, or news and alerts. In a site built for something such as a professional sports team, the search function must be able to dig out news, results, athlete names, bios and so forth. The data structure upon which a search function has to work is never obvious. It’s clearly application-specific.

Instead of reinventing the wheel every time, consider using an ad hoc full-text search engine such as Lucene.Net to back up your search function. An engine like Lucene.Net indexes a bunch of string-based documents and parses any given query string against the index. In doing so, the engine lets you specify complex combinations of query strings. For many pages and sites, Lucene.Net might be overkill, yet they still need some type of search that’s smarter than placing an endless list of items in a dropdown.

This article will present a small framework for auto-completion built around Twitter typeahead.js. This framework isn’t magical, but it truly simplifies using auto-completion in Web pages. The most enticing aspect of this framework is it lets you combine multiple datasets to query within the same page and retrieve distinct but related information.

Setting Up Typeahead.js

For this article, I’ll clarify the basics of using typeahead in a realistic scenario using the version of typeahead you find on NuGet when typing “typeahead,” as shown in Figure 1. When you Google search typeahead, you might run into older references, older JavaScript files or just forked versions of the original project code. Documentation is misleading, as well.

The NuGet Package for Twitter Typeahead.js
Figure 1 The NuGet Package for Twitter Typeahead.js

The bundle file contains all packages that make up the library, including the Bloodhound engine to manage hints on the local browser. To set up a Web page or Razor view to use typeahead.js, you just need the familiar jQuery-like syntax and activate the plug-in on the selected input field. Here’s an example:

<form action="@Url.Action("Query", "Home")" method="post">

  <input type="hidden" id="queryCode" name="queryCode" />

    <input type="text" name="queryString" id="queryString">

    <button id="queryButton" type="submit">Get</button>

</form>

<form action="@Url.Action("Query", "Home")" method="post">
  <input type="hidden" id="queryCode" name="queryCode" />
    <input type="text" name="queryString" id="queryString">
    <button id="queryButton" type="submit">Get</button>
</form>

It’s important to note that in order to use auto-completion on a Web page for something useful, you also need a hidden buddy field to collect some sort of unique ID for the selected hint. There are many scenarios in which you may consider using an auto-completed input field. The scenario I’m interested in uses auto-completion to replace an otherwise endless dropdown list. It enables lightweight, Bing-style search in your site, without requiring the help of full-text engines such as Lucene.Net.

Script Code to Have in the View

To use typeahead.js, reference jQuery 1.9.1 or superior and the typeahead script. The minimal amount of code you you’ll need in the view is shown here:

$('#queryString').typeahead(
  null,
  {
    displayKey: 'value',
    source: hints.ttAdapter()
  }
});

In doing so, you take all default settings and instruct the engine to use the value property in returned data for filling the dropdown list. A property named value is assumed to exist in any data you filter. In theory, you can set up auto-completion on any JavaScript data array. In practice, though, auto-completion makes sense mostly when data is downloaded from a remote source.

Downloading from a remote source poses many issues—same-­origin browser policy, prefetching and caching—to name a few. Twitter typeahead.js comes with a suggestion engine named Bloodhound. This transparently does most of this work for you. If you get the bundle JavaScript file from NuGet, you can just start calling into Bloodhound without worrying about its download and setup. The hints variable in the previous code snippet results from the following and rather standard initialization of Bloodhound:

var hints = new Bloodhound({
  datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
  queryTokenizer: Bloodhound.tokenizers.whitespace,
  remote: "/hint/s?query=%QUERY"
});
hints.initialize();

Note the remote attribute. That’s just the server endpoint responsible for returning hints to display in the dropdown list. Also note the syntax %QUERY. This indicates the string in the input field being sent to the server for hints. In other words, %QUERY is a placeholder for whatever text is in the input field. By default, typeahead.js starts getting hints as soon as you type a single character. If you want to wait for characters to be in the buffer before auto-completion starts, then add a settings object as the first argument of the plug-in:

$('#queryString').typeahead(
  {
    minLength: 2
  },
  {
    displayKey: 'value',
    source: hints.ttAdapter()
  }
});

When the buffer is full enough to start remote calls, Bloodhound begins to work. It downloads JSON data and adapts it for display. At this point, you have a barely working auto-completion engine that pops up suggestions based on some logic you have on the server. However, there’s a lot more to do before you can use auto-completion effectively in a real page.

Use Typeahead.js with Bootstrap

Any sufficiently complex plug-in needs a bit of CSS to look nice. Typeahead.js is no exception. The plug-in comes with its own default UI, but you might want to apply some fixes, especially if you use it with Twitter Bootstrap. You might also want to customize some visual attributes, such as colors and padding. Figure 2 lists some CSS classes you might want to use to personalize the look-and-feel of the typeahead.js component.

Figure 2 The CSS Classes to Edit in Order to Customize the Typeahead.js Component

CSS Class Description
twitter-typeahead Styles the input field where the user types hints.
tt-hint Styles the text that represents the delta between what you typed and the first hint. This class is only used when the hint property is set to true (false by default).
tt-dropdown-menu Styles the dropdown popup where hints are listed.
tt-cursor Styles highlighted suggestions in the dropdown box.
tt-highlight Styles the portion of the text that matches the query string.

Figure 3 gives an idea of what you can get out of custom CSS classes. You can also customize the overall plug-in behavior from a functional standpoint.

Customized CSS Classes Can Accomplish Different Effects for Your App
Figure 3 Customized CSS Classes Can Accomplish Different Effects for Your App

Add Client-Side Logic

An auto-complete field is faster than any long dropdown. When the number of items from which to choose is in the hundreds, though, any classic dropdown list is slow. Therefore, if you plan to use the auto-complete input field to select a specific value—say, the name of a product or a customer—then a plain typeahead.js plug-in isn’t enough. Some additional script code is required that binds to the selected event of the plug-in:

$('#queryString').on('typeahead:selected', 
  function (e, datum) {
  $("#queryCode").val(datum.id);
});

The major benefit of auto-completion input is users type some intelligible name and the system retrieves the affiliated unique code or ID. This feature has to be explicitly coded. In the selected event handler, you retrieve the ID information from the datum object and store it safely in a hidden field. When the form to which the auto-completion input fields belong is posted, the selected ID is posted, as well. The format of the datum object—the data item being selected from the dropdown—depends on the format of the data you receive from the server side.

What about the text being displayed in the input field? In this scenario, you probably don’t need to have any significant text displayed in the input field. The relevant input for further operations is what you store in the hidden field. What you display there is up to you. Just notice if you specify in the plug-in settings a displayKey property, because then the value of that property is automatically displayed in the input field. At any rate, you can set any value programmatically:

$("#queryString").val(datum.label);

In some cases, the auto-completion textbox is the only element in HTML form. This means you might want to process any selected data as soon as it’s selected. By adding the following line to the typeahead.js selected event handler, you simulate a click on the submit button of the form:

$("#queryButton").click();

Suppose a user starts typing in the input field, causes the dropdown to display and then stops typing without making a selection. What should you do when he resumes typing? Naturally, you would let him type again until he makes a selection. Once the user makes a selection, some code has been stored so when editing resumes that selection must be canceled. You need a local variable to achieve this:

var typeaheadItemSelected = false;
$('#queryString').on('typeahead:selected', function (e, datum) {
  $("#queryCode").val(datum.id);
  typeaheadItemSelected = true;
});

You also need a handler for the focus event of the input field to reset any stored data:

$('#queryString').on('input', function () {
  if (typeaheadItemSelected) {
    typeaheadItemSelected = false;
    $('#queryString').val(''); 
    $("#queryCode").val('');
  }
});

The ultimate purpose of this extra client-side logic is to ensure the auto-completion input field works the same as a dropdown list.

The Server Side of Auto-Complete

Whatever any code on the client side can do strictly depends on the data returned from the server. At the very minimum, the server endpoint is just a URL that returns JSON data. Once you have an endpoint that serves a collection of Product objects, for example, you can use the displayKey property of typeahead.js to perform some sort of data binding on the dropdown list of hints. In its simplest form, a JSON-returning controller method can look like this:

public JsonResult P(string query)
{
  var productHints = _service.GetMatchingProducts(query);
  return Json(productHints, JsonRequestBehavior.AllowGet);
}

If the auto-completion input field is expected to display hints for homogeneous data items, this is an ideal approach. On the client side, in fact, you can easily leverage the templating mechanism built into typeahead.js and arrange custom suggestion views:

$('#queryString').typeahead(
null,
{
  templates: {
    suggestion: Handlebars.compile('<b>({{Id}}</b>: {{notes}}')
  },
  source: hints.ttAdapter()
});

The templates property replaces displayKey and sets a custom layout for the dropdown content. The dropdown in Figure 3 results from the previous code snippet. When arranging a template, you might want to use an ad hoc template engine such as Handlebars (handlebarsjs.com). You must link Handlebars to the project separately from typeahead.js. Using Handlebars is optional. You can always format an HTML template through manual JavaScript code or even return a server-side object with preformatted HTML.

Things get trickier when your auto-completion input is expected to return heterogeneous hints, such as products or offers. In this case, you must return an array of some intermediate data type that contains enough information for the user to choose. The auto-completion you get with this article offers a basic AutoCompleteItem class, as shown here:

public class AutoCompleteItem
{
  public String label { get; set; }
  public String id { get; set; }
  public String value { get; set; }
}

The id property contains a unique ID. When posted, that is meaningful to the receiving controller. It’s typically made of two pieces—the actual ID and an identifier that uniquely matches the ID to one of the datasets (products or offers) possibly being returned in the query. The property value is the string content to show in the dropdown list. The other property is sort of a cargo property for whatever else you might need. The value property can also contain server-side-arranged HTML strings for custom suggestion layouts. The server-side code is also responsible for running all required queries and massaging data into a collection of AutoCompleteItem objects.

Wrapping Up

Usability is more important every day in modern Web sites. It’s welcome in the public face of the site, but is even more critical in Web site back ends where the actual information is inserted. On the admin side of one of my Web sites, I once had a dropdown list with more than 700 items. It worked, but it was slow. I replaced it with auto-completion and now it’s blazingly fast. I arranged a GitHub project for this and other utilities at bit.ly/1zubJea.


Dino Esposito is the co-author of “Microsoft .NET: Architecting Applications for the Enterprise” (Microsoft Press, 2014) and “Programming ASP.NET MVC 5” (Microsoft Press, 2014). A technical evangelist for the Microsoft .NET Framework and Android platforms at JetBrains and frequent speaker at industry events worldwide, Esposito shares his vision of software at software2cents.wordpress.com and on Twitter at twitter.com/despos.

Thanks to the following technical expert for reviewing this article: Jon Arne Saeteras