Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to customize Bootstrap typeahead layout/function for more than just text?

I'm working on a search functionality for my website and I'm using Bootstrap's typeahead to show the results. So far so good. But what I want is to extend the function so that I could add an image and some other item, say description, etc. According to this post, that is possible: custom typeahead.

Basically I need something like this as each result item:

<div class="resultContainer">
  <div class="resultImage"><img /></div>
  <div class="resultDesc"></div>
  <div class="resultLabel"></div>
</div>

Now it's:

<li><a href="#"></a></li>

And this doesn't even work with a longer text than the width of the typeahead. So the text doesn't go to the next line and hence doesn't fit the <li>. How can I use the the custom layout I want? (btw I have an array of arrays of results. Each sub-array contains img, desc, etc.). This is what I have right now: enter image description here

Thanks in advance.

like image 815
Loolooii Avatar asked Jun 03 '12 19:06

Loolooii


3 Answers

Use the highlighter method:

$('.typeahead').typeahead({
    highlighter: function(item){
        return "<div>.......</div>";
    }
});

Method used to highlight autocomplete results. Accepts a single argument item and has the scope of the typeahead instance. Should return html.

like image 122
Esailija Avatar answered Oct 16 '22 08:10

Esailija


mgadda's answer neatly solves the issue for how to augment rendering items with custom data. Unfortunately this metadata isn't available once an item is selected, because bootstrap's select() method calls your updater() with a value taken from the DOM, not your loaded String:

var val = this.$menu.find('.active').attr('data-value');

One way around this is to set the metadata you need to DOM elements in highlighter(), then find them in your updater(). Since we only needed an integer, we set an ID to the image tag itself, and in updater, find it based on the image tag that's still visible:

$('.your_textboxes').typeahead({
  highlighter: function(item) {
    return('<img src="' + item.friend.img + '" id="klid_' + item.friend.id + '" />' + item);
  },
  updater: function(val) {
    var klid = $('ul.typeahead li.active:visible a img');
    if (klid && klid.length) {
      klid = klid[0].id.substr(5);
      // do stuff with metadata...
    }
    return(val);
}

Need more metadata? Wrap your highlighter in a span, or add some hidden form fields.

This feels kludgey though. If anyone has a cleaner solution, I'd love to see it.

like image 10
hoodslide Avatar answered Oct 16 '22 07:10

hoodslide


Typeahead expects that everything passed into the process callback from the source callback will be a string. Unfortunately, this means that your highlighter callback is somewhat limited in the kind of HTML it can generate since you can only modify a string so much.

You can, however, define your source method such that it returns the usual array of string objects each of which have an additional data property that contains all the data you'll need to generate HTML in the highlighter.

$('.typeahead').typeahead
  source: (query, processCallback)->
    $.get '/users', q: query, (data)->
      processCallback data.map (user)->
        item = new String("#{user.first_name} #{user.last_name}")
        item.data = user
        item

In this form, item will be passed around the internals of typeahead as a string, and when it finally reaches your custom highlighter, you can use the data property to build more complex html:

$('.typeahead').typeahead
  highlighter: (item)->
    "<img src=\"#{item.data.avatar_url}\" /> #{item}"
like image 8
mgadda Avatar answered Oct 16 '22 07:10

mgadda