Building a Typeahead Directive with AngularJS

  • As an example, if I define my html as such:

    And then the template of the component is this:

    The final result with a list of artists would be:

    This is exactly what I want.

  • I need the html and the component to talk to each other so that when the user types something the proper item in the list is highlighted, or if the user clicks on an item in the list it is selected.
  • The issue is that the component places no constraints on the structure of the rendered list so it has no idea which item model corresponds to which html element.
  • The directive responsible for each typeahead item is called (yeah, I’m great at names), which is placed on each selectable item within the list like so:

    The directive links a given model (the artist) to the corresponding html (the element).

  • The end result is that the component is able to highlight and select items attached to any arbitrary html elements.

I am building a little project that lets the user keep a list of artists and albums and I needed a typeahead / autocomplete that would match on such things. I took inspiration from the last.fm typeahead:

@KingsleyTagbo: How To Build an AngularJS Typeahead Plugin RT @hueypetersen #html5 #bootstrap #angularjs #angular

I am building a little project that lets the user keep a list of artists and albums and I needed a typeahead / autocomplete that would match on such things. I took inspiration from the last.fm typeahead:

I am also using the last.fm api.

My first thought was to wrap the Twitter Bootstrap typeahead in Angular. The problem is I wanted full control over the html in the typeahead. For instance I wanted little sidebars that said ‘artists’ and ‘albums’. I had no idea how to do this with the Bootstrap typeahead. Another problem is from a purely aesthetic point of view I don’t like wrapping things in Angular if the problem itself is easily solved using Angular.

I thought this would be a good project to tackle with Angular so I wrote my own.

The starting point was to create a custom component. Angular lets you define your own html elements and so the

element was born!

It doesn’t do much yet.

Many typeaheads let you control the html of individual result items by passing a string of html to render. That isn’t very Angular. It also doesn’t fully get me what I want since I need those ‘artist’ and ‘album’ sidebars which aren’t part of any single element. What I really want is an Angular template with all of the normal directives at my disposal. Then I want this html shoved into the typeahead control which can control the typeahead-y things like when its visible, selecting items, and what not.

So Angular has this idea of transclusion, which sounds quite fancy. And it is! Transclusion lets you use the html content of your custom component within the componenet template itself. This means I can have full control over the html of the typeahead while letting the shared behavior live within the typeahead component.

As an example, if I define my html as such:

component is this:

The final result with a list of artists would be:

This is exactly what I want. I’m able to use the full power of Angular to create my list and the component takes care of the rest. The key thing to note is the

template. This is where the inner html from the component will be shoved… er, transcluded.

You also have to configure the componenet directive to use transclusion.

At this point I have the html I’m after, but unfortunately it doesn’t do anything. I need the html and the

component to talk to each other so that when the user types something the proper item in the list is highlighted, or if the user clicks on an item in the list it is selected.

and the second item in the list is highlighted (Black Sabbath) and the first one loses highlight. This is the behavior you would expect out of a typeahead.

component places no constraints on the structure of the rendered list so it has no idea which item model corresponds to which html element. We need a way to link the items in the list together so that they can communicate on which one needs to be highlighted.

One way to accomplish this is with a directive controller. The typeahead component (which is a directive in case that isn’t clear) can declare a controller which other directives can request access to. This controller allows communication between the directives – namely which item is currently highlighted.

(yeah, I’m great at names), which is placed on each selectable item within the list like so:

li

directives watch this value with a comparison on their own item and act accordingly. In my case the action is just adding or removing a class.

Heres the code:

in

require: ‘^typeahead’

instructs Angular to look on parent elements until it finds the controller.

(I apparently lack consistent naming), and sets a class accordingly. This class could be made configurable pretty easily but its fine for now.

There are then two event listeners which communicate back to the controller when the item is clicked on (select it) or hovered over (activate / highlight it).

component contains a bunch of code in its linker / controller but its pretty basic typeahead stuff and agnostic to how the list is rendered.

I love that customizing the look of the typeahed list consists of writing the same type of Angular code I use to customize how anything looks. I don’t have to learn the magic configuration language of a plugin. Win.

component wired up.

Gist of the code.

And with results:

Its like the last.fm one but uglier. Mission accomplished.

UPDATE: I pushed this demo to heroku and code is on github.

Building a Typeahead Directive with AngularJS

You might also like More from author

Comments are closed, but trackbacks and pingbacks are open.