RSS

CEK.io

Chris EK, on life as a continually learning software engineer.

Typeahead: A How-To Guide (Part 2 of 2)

Note: If you haven’t already read Part 1, which covers the Gon gem in detail, you can see it here. In this post, I’ll cover the .typeahead JavaScript method.

The How-To (cont.)

    Typeahead.js depends on having a source of data available. In fact, Twitter’s “basic” typeahead example includes var states, with all 50 states input as strings, directly in the JavaScript file. That’s effectively what I did in the previous post with Gon: I made Rails variables available in JavaScript. I’ll use those same variables here.

    2. Typeahead.js

  • Add the gem: gem 'twitter-typeahead-rails'.

  • Add //= require twitter/typeahead to the application.js manifest.

  • Determine the css selector for the input on which you want to implement typeahead. In my case, I added a class of “typeahead” to my targeted text field as follows:
    (form.html.erb) download
    1
    2
    3
    
    <!-- ...preceding html/erb... -->
    <%= text_field_tag :name_or_email, nil, placeholder: "To: name or email", class: "typeahead" %> 
    <!-- ... -->
    

  • In whichever JavaScript file you’ve made the dataset available, add $('.typeahead').typeahead(). Replace “.typeahead” with whatever CSS selector you’re using (see previous step). You now have the entire structure into which you can try the various typeahead.js examples.

  • I started with the basics, copying line 1-23 above my typeahead method, and everything withing the curly braces from line 37-44 into my typeahead method. Make sure to change the example’s default variable (“states” in the basic example, “best-pictures” in the custom templates example) to whatever variable you assigned using Gon. Depending on how your Rails app is structured–particularly what your asset pipeline looks like–this may be all you need.

  • I had conflicting stylesheets and JavaScript files, plus I wanted more flexibility creating templates for the typeahead, so I implemented the custom templates example. Full code for what I describe is below.

    To begin, I added the templates (lines 28-40). The “empty” template worked easily–the code copied from Twitter’s example worked on its own. I had more difficulty with the “suggestion” template, primarily because I didn’t immediately realize that it’s a function, as opposed to the string that the “empty” template was. At this point, I was able to style the template as I liked, even including an image.

    Next I had to configure the typeahead to use the pictures connected to each user. Gon and typeahead seem to support a pattern of passing entire objects and calling two or more different attributes (best pictures’ names and years in the custom template example), but I was unable to configure my JavaScript this way. Instead, knowing that my “gravatars” array directly mirrored my “usernames”, I used JavaScript’s indexOf() method, as seen in line 37. This enabled me to the the index of a given username and find the gravatar url at that index of the gravatars array. Not ideal, but it worked.

    (typeahead.js) download
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    
    // Twitter Typeahead
    $(document).ready(function() {
      var substringMatcher = function(strs) {
        return function findMatches(q, cb) {
          var matches, substringRegex;
          matches = [];
          substrRegex = new RegExp(q, 'i');
          $.each(strs, function(i, str) {
            if (substrRegex.test(str)) {
              matches.push({ value: str });
            }
          });
          cb(matches);
        };
      };
      var gravatars = gon.gravatars;
      var usernames = gon.usernames;
      $('.typeahead').typeahead({
        hint: true,
        highlight: true,
        minLength: 1,
      },
    
      {
        name: 'usernames',
        displayKey: 'value',
        source: substringMatcher(usernames),
        templates: {
          empty: [
            '<div class="empty-message">',
            'No username matches found. Enter an email instead!',
            '</div>'
          ].join('\n'),
          suggestion: function(username){
            return  '<div id="user-selection">' +
                    '<p><strong>' + username.value + '</strong></p>' +
                    '<img src="' + gravatars[usernames.indexOf(username.value)] + '"/>' +
                    '</div>' ;
          }
        }
      });
    });
    

  • My final issue came with the highlighting. The highlight:true line within the typeahead method indicates that it should work right out of the box, but it did not for me (again, due to conflicting other assets). For this reason, I needed to debug in my JavaScript console, eventually finding that an item selected using typeahead was situated in a div with class of “tt-cursor”. A lot of the default styling was fine with me but, since the highlighting itself did not work, I added the following to my css, thus giving the selected item a background color. “` css styling.css div .tt-cursor { background-color: #16A085; } “` (that selector, “.tt-cursor”, was very important!)

And that concludes my adventures with typeahead. It took some time, but, as I described before, it’s those seemingly minor finishing touches that can end up taking the most time. This exercise was certainly proof of that. But it was worth it to get a working—and good-looking—typeahead form input.

Resources