285

I'm trying to find a working example of the twitter bootstrap typeahead element that will make an ajax call to populate it's dropdown.

I have an existing working jquery autocomplete example which defines the ajax url to and how to process the reply

<script type="text/javascript">
//<![CDATA[
$(document).ready(function() {
    var options = { minChars:3, max:20 };
    $("#runnerquery").autocomplete('./index/runnerfilter/format/html',options).result(
            function(event, data, formatted)
                {
                    window.location = "./runner/index/id/"+data[1];
                }
            );
       ..

What do i need change to convert this to the typeahead example?

<script type="text/javascript">
//<![CDATA[
$(document).ready(function() {
    var options = { source:'/index/runnerfilter/format/html', items:5 };
    $("#runnerquery").typeahead(options).result(
            function(event, data, formatted)
                {
                    window.location = "./runner/index/id/"+data[1];
                }
            );
       ..

I'm going to wait for the 'Add remote sources support for typeahead' issue to be resolved.

user
  • 17,781
  • 20
  • 98
  • 124
emeraldjava
  • 10,894
  • 26
  • 97
  • 170
  • To be more specific i'm wondering how the autocomplete options and result handling function map to the textahead options? Is there a set of defined textalert result handling functions that can be overridden, or are the method named inherited from the underlying jquery api. – emeraldjava Feb 10 '12 at 22:46
  • 1
    Could you mark Stijn Van Bael's answer as the correct one now? bogert's answer only works for out of date versions of Bootstrap. – Giles Roberts Jun 15 '13 at 08:31

17 Answers17

304

Edit: typeahead is no longer bundled in Bootstrap 3. Check out:

As of Bootstrap 2.1.0 up to 2.3.2, you can do this:

$('.typeahead').typeahead({
    source: function (query, process) {
        return $.get('/typeahead', { query: query }, function (data) {
            return process(data.options);
        });
    }
});

To consume JSON data like this:

{
    "options": [
        "Option 1",
        "Option 2",
        "Option 3",
        "Option 4",
        "Option 5"
    ]
}

Note that the JSON data must be of the right mime type (application/json) so jQuery recognizes it as JSON.

Community
  • 1
  • 1
Stijn Van Bael
  • 4,830
  • 2
  • 32
  • 38
  • Thanks for mentioning this. I am trying to find an example for exactly this. So the data returned through $.get has better to be JSON right? – Houman Sep 04 '12 at 19:16
  • 3
    As in the Typeahead fork, data must be a JSON array of strings and the content type must be application/json. – Stijn Van Bael Sep 04 '12 at 20:00
  • 9
    can 2.1 use json that isnt just a string array? i need to so a value to the user and use an id to do further processing. Is that possible without pickingup a custom fork? – Anton Sep 09 '12 at 23:35
  • @Anton: you could do the processing in the anonymous function before calling process(...) – Stijn Van Bael Sep 21 '12 at 14:07
  • 2
    @Stijin Are there any examples of how that anonymous function would be used here, to process an id for a displayed option? Thanks! – Acyra Feb 03 '13 at 00:39
  • 1
    I would like to point out that using this method results in an AJAX called being made for _every_ keystroke on the input. If the server is returning essentially static data (i.e. not really doing anything with the query), then this can be quite wasteful. – Dologan Feb 08 '13 at 18:15
  • My solution was to make it return a previously declared global variable, which is populated upon the first call: `var opt_arr; $('.typeahead').typeahead({ source: function (useless, callback) { if (opt_arr) { return opt_arr; } else { $.getJSON('/static_json', function(data) { opt_arr = data; callback(opt_arr); }); } } }); ` – Dologan Feb 08 '13 at 18:23
  • The one thing that tripped me up was leaving the data-provide="typeahead" attribute in the element. The AJAX call won't be made with that attribute in place. I forgot to remove it after testing a more simple example. – yukondude Mar 25 '13 at 02:41
  • 2
    why use `get` instead of `getJSON` ? It seems more appropriate. – greg0ire May 21 '13 at 10:10
  • @StijnVanBael ,Excuse me ... as you said bootsrap 3no longer supports this so by your idea whats better for doing such thing in web? – neda Derakhshesh Jul 10 '16 at 12:16
  • Using typeahead.js would be the way to go – Stijn Van Bael Jul 11 '16 at 11:43
  • I used this solution successfully even with Bootstrap 3. The function's structure is this: `source : function(q, pr) {return $.get(searchUrl, {query:q}, function(resp) {return pr(resp);}); }` – twigmac Mar 10 '17 at 15:11
  • Update on my comment: I've been using [Bootstrap-3-Typeahead](https://github.com/bassjobsen/Bootstrap-3-Typeahead) for this. – twigmac Mar 10 '17 at 15:43
118

You can use the BS Typeahead fork which supports ajax calls. Then you will be able to write:

$('.typeahead').typeahead({
    source: function (typeahead, query) {
        return $.get('/typeahead', { query: query }, function (data) {
            return typeahead.process(data);
        });
    }
});
Stijn Van Bael
  • 4,830
  • 2
  • 32
  • 38
frm
  • 1,720
  • 1
  • 14
  • 18
  • 1
    jQuery's "intelligent guess" for the return type of the POST data was not working when I returned, for example, `["aardvark", "apple"]`. I had to explicitly set the `dataType` parameter in the `$.post` call. See [jQuery.post()](http://api.jquery.com/jQuery.post/). – Rusty Fausak Mar 19 '12 at 20:47
  • 1
    @rfausak Alternatively, set the `Content-type` header to `application/json` – Rusty Fausak Mar 19 '12 at 20:51
  • 23
    i am getting Uncaught TypeError: Cannot call method 'toLowerCase' of undefined – Krishna Prasad Varma Jun 04 '12 at 10:00
  • In the link you sent, I'm not even getting the `source` function to execute. – James Jun 13 '12 at 14:20
  • I solved the problem "Cannot call method 'toLowerCase' of undefined" by playing around with the loading order of the js scripts (jquery, bootstrap, typeahead). – javanna Jul 13 '12 at 14:57
  • I think the async call should be $.getJSON – Tony Aug 01 '12 at 20:16
  • 8
    Nice answer! I would use a GET request instead, just to meet REST standards. – Mauro Aug 30 '12 at 22:45
  • hi, I found this solution to be parse an error, and http://stackoverflow.com/questions/11178512/twitter-bootstrap-typeahead-2-0-4-ajax-error solution to be helpful – devmonster Dec 02 '12 at 15:08
  • simple example to create smart autocomplete with twitter bootstrap's typeahead. I needed this for a project. Enjoy. https://gist.github.com/mrgcohen/5062352 – mc. Mar 01 '13 at 03:56
71

Starting from Bootstrap 2.1.0:

HTML:

<input type='text' class='ajax-typeahead' data-link='your-json-link' />

Javascript:

$('.ajax-typeahead').typeahead({
    source: function(query, process) {
        return $.ajax({
            url: $(this)[0].$element[0].dataset.link,
            type: 'get',
            data: {query: query},
            dataType: 'json',
            success: function(json) {
                return typeof json.options == 'undefined' ? false : process(json.options);
            }
        });
    }
});

Now you can make a unified code, placing "json-request" links in your HTML-code.

Thantifaxath
  • 719
  • 5
  • 3
54

All of the responses refer to BootStrap 2 typeahead, which is no longer present in BootStrap 3.

For anyone else directed here looking for an AJAX example using the new post-Bootstrap Twitter typeahead.js, here's a working example. The syntax is a little different:

$('#mytextquery').typeahead({
  hint: true,
  highlight: true,
  minLength: 1
},
{
  limit: 12,
  async: true,
  source: function (query, processSync, processAsync) {
    processSync(['This suggestion appears immediately', 'This one too']);
    return $.ajax({
      url: "/ajax/myfilter.php", 
      type: 'GET',
      data: {query: query},
      dataType: 'json',
      success: function (json) {
        // in this example, json is simply an array of strings
        return processAsync(json);
      }
    });
  }
});

This example uses both synchronous (the call to processSync) and asynchronous suggestion, so you'd see some options appear immediately, then others are added. You can just use one or the other.

There are lots of bindable events and some very powerful options, including working with objects rather than strings, in which case you'd use your own custom display function to render your items as text.

Jonathan Lidbeck
  • 1,555
  • 1
  • 14
  • 15
  • 1
    Thank you. One more question: with processAsync I'm getting "TypeError: suggestions.slice is not a function." What would the returned JSON need too look like? Here's my best guess: { :suggestions => ["Thing 1","Thing 2","Thing 3"] } – user1515295 Nov 26 '15 at 04:46
  • 1
    Make sure you return a valid JSON string. Your AJAX suggestion function should return an array of strings, e.g. `["Thing 1","Thing 2"]`, or with a custom display function, an array of objects according to your needs, e.g. `[{"id":1,"label":"Foo"},{"id":2,"label":"Bar"}]` – Jonathan Lidbeck Nov 27 '15 at 07:12
  • I return `[{"id":1,"label":"Foo"},{"id":2,"label":"Bar"}]`, Now I want to show two columns in typeahead dropdown with id and label. How can I do that? – Vishal Feb 26 '17 at 16:02
  • 3
    MY GOD!!! I was searching from past 3 days no one mentioned about this. I was testing with sync function till now. THANKS DUDE!! – Gilson PJ Jan 24 '18 at 09:06
  • Thanks ! Works great with bootstrap 4.3, and jquery 3.4. But, the listed options don't get highlighted when the mouse hovers over it. I thought it was supposed to be part of typeahead itself. – Binita Bharati Oct 10 '19 at 03:09
24

I've augmented the original typeahead Bootstrap plugin with ajax capabilities. Very easy to use:

$("#ajax-typeahead").typeahead({
     ajax: "/path/to/source"
});

Here's the github repo: Ajax-Typeahead

Eonasdan
  • 7,563
  • 8
  • 55
  • 82
Paul Warelis
  • 349
  • 5
  • 8
  • I've looked at Gudbergur's code; frankly, I liked this one best. It's a bit more friendly and offers more functionality. Good job Paul! Only thing I would suggest is in your README remind the user that they need to parse their JSON-data into JS in order for your code to be able to use it correctly. I'd assumed you were parsing it for me so that hung me up for a bit. Otherwise, pretty nice, thank you! :) – Bane May 10 '12 at 18:35
  • 3
    Your server returns a string instead of a JSON object. jQuery's $.ajax() is used to make the call, and it takes care of the parsing. – Paul Warelis May 11 '12 at 22:27
  • 1
    absolutely correct, thank you for the catch! :) This plugin works very well. – Bane May 14 '12 at 19:11
  • Hello there, how should server side `JSON` look like? I am getting `Uncaught TypeError: Cannot read property 'length' of undefined `. I am using `json_encode($array)` and sending right headers (`'Content-Type: application/json; charset=utf-8'`). jquery version `jQuery v1.9.1` – Kyslik Jul 10 '13 at 10:23
5

I did some modifications on the jquery-ui.min.js:

//Line 319 ORIG:
this.menu=d("<ul></ul>").addClass("ui-autocomplete").appendTo(d(...
// NEW:
this.menu=d("<ul></ul>").addClass("ui-autocomplete").addClass("typeahead").addClass("dropdown-menu").appendTo(d(...

// Line 328 ORIG:
this.element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr...
// NEW:this.element.attr....

// Line 329 ORIG:
this.active=a.eq(0).children("a")
this.active.children("a")
// NEW:
this.active=a.eq(0).addClass("active").children("a")
this.active.removeClass("active").children("a")`

and add following css

.dropdown-menu {
    max-width: 920px;
}
.ui-menu-item {
    cursor: pointer;        
}

Works perfect.

El Developer
  • 3,345
  • 1
  • 21
  • 40
bmoers
  • 51
  • 3
  • which version of jquery ui min are you using? – emeraldjava Feb 14 '12 at 10:30
  • If you need this for jQuery 1.7.1 and jQuery UI 1.8.16 then I have created a GIST based on the above fix that shows where to change the jQuery UI File. https://gist.github.com/1884819 - lines are commented with //modified – RidingTheRails Feb 22 '12 at 12:35
  • This is a pretty old question/answer but I just want to say that it's always a bad practice to change the 3rd party components code. You'll have to revisit all the changes each time you upgrade them. In this particular case, jQuery widgets can be inherited which is a much safer way to customize them. So basically, create your own widget, inherit from the core one (the autocomplete in this case) and unleash your imagination! :) – AlexCode Apr 22 '14 at 11:48
2

One can make calls by using Bootstrap. The current version does not has any source update issues Trouble updating Bootstrap's typeahead data-source with post response , i.e. the source of bootstrap once updated can be again modified.

Please refer to below for an example:

jQuery('#help').typeahead({
    source : function(query, process) {
        jQuery.ajax({
            url : "urltobefetched",
            type : 'GET',
            data : {
                "query" : query
            },
            dataType : 'json',
            success : function(json) {
                process(json);
            }
        });
    },
    minLength : 1,
});
Community
  • 1
  • 1
neoeahit
  • 1,689
  • 2
  • 21
  • 35
2

I am using this method

$('.typeahead').typeahead({
    hint: true,
    highlight: true,
    minLength: 1
},
    {
    name: 'options',
    displayKey: 'value',
    source: function (query, process) {
        return $.get('/weather/searchCity/?q=%QUERY', { query: query }, function (data) {
            var matches = [];
            $.each(data, function(i, str) {
                matches.push({ value: str });
            });
            return process(matches);

        },'json');
    }
});
Krava
  • 29
  • 1
  • It would be nice if you could add some explanation to your answer. For example, what is the difference of your solution compared to the solutions presented in the already existing answers? – honk Dec 22 '14 at 19:22
2

Try this if your service is not returning the right application/json content type header:

$('.typeahead').typeahead({
    source: function (query, process) {
        return $.get('/typeahead', { query: query }, function (data) {
            var json = JSON.parse(data); // string to json
            return process(json.options);
        });
    }
});
Andres
  • 1,090
  • 14
  • 30
1

UPDATE: I modified my code using this fork

also instead of using $.each I changed to $.map as suggested by Tomislav Markovski

$('#manufacturer').typeahead({
    source: function(typeahead, query){
        $.ajax({
            url: window.location.origin+"/bows/get_manufacturers.json",
            type: "POST",
            data: "",
            dataType: "JSON",
            async: false,
            success: function(results){
                var manufacturers = new Array;
                $.map(results.data.manufacturers, function(data, item){
                    var group;
                    group = {
                        manufacturer_id: data.Manufacturer.id,
                        manufacturer: data.Manufacturer.manufacturer
                    };
                    manufacturers.push(group);
                });
                typeahead.process(manufacturers);
            }
        });
    },
    property: 'name',
    items:11,
    onselect: function (obj) {

    }
});

However I am encountering some problems by getting

Uncaught TypeError: Cannot call method 'toLowerCase' of undefined

as you can see on a newer post I am trying to figure out here

hope this update is of any help to you...

Community
  • 1
  • 1
mmoscosa
  • 297
  • 1
  • 3
  • 13
  • Unrelated to OP: I would use `Array.map` instead `$.each` and replace the content of your whole `success` callback function with `var manufacturers = results.data.manufacturers.map(function (item) { return { id: item.Manufacturer.id, manufacturer: item.Manufacturer.manufacturer } });` – Tomislav Markovski Jun 27 '12 at 00:29
  • thank you @TomislavMarkovski I modified my code as you suggest. – mmoscosa Jun 27 '12 at 22:30
  • For the latest version, use "display" instead of "property" – Dhara Jan 05 '21 at 07:48
1

To those looking for a coffeescript version of the accepted answer:

$(".typeahead").typeahead source: (query, process) ->
  $.get "/typeahead",
    query: query
  , (data) ->
    process data.options
Hendrik
  • 4,849
  • 7
  • 46
  • 51
1

I went through this post and everything didnt want to work correctly and eventually pieced the bits together from a few answers so I have a 100% working demo and will paste it here for reference - paste this into a php file and make sure includes are in the right place.

<?php if (isset($_GET['typeahead'])){
    die(json_encode(array('options' => array('like','spike','dike','ikelalcdass'))));
}
?>
<link href="bootstrap.css" rel="stylesheet">
<input type="text" class='typeahead'>
<script src="jquery-1.10.2.js"></script>
<script src="bootstrap.min.js"></script>
<script>
$('.typeahead').typeahead({
    source: function (query, process) {
        return $.get('index.php?typeahead', { query: query }, function (data) {
            return process(JSON.parse(data).options);
        });
    }
});
</script>
l0ft13
  • 710
  • 1
  • 7
  • 11
1

i use $().one() for solve this; When page loaded, I send ajax to server and wait to done. Then pass result to function.$().one() is important .Because force typehead.js to attach to input one time. sorry for bad writing.

(($) => {
    
    var substringMatcher = function(strs) {
        return function findMatches(q, cb) {
          var matches, substringRegex;
          // an array that will be populated with substring matches
          matches = [];
      
          // regex used to determine if a string contains the substring `q`
          substrRegex = new RegExp(q, 'i');
      
          // iterate through the pool of strings and for any string that
          // contains the substring `q`, add it to the `matches` array
          $.each(strs, function(i, str) {
            if (substrRegex.test(str)) {
              matches.push(str);
            }
          });
          cb(matches);
        };
      };
      
      var states = [];
      $.ajax({
          url: 'https://baconipsum.com/api/?type=meat-and-filler',
          type: 'get'
      }).done(function(data) {
        $('.typeahead').one().typeahead({
            hint: true,
            highlight: true,
            minLength: 1
          },
          {
            name: 'states',
            source: substringMatcher(data)
          });
      })
      

})(jQuery);
.tt-query, /* UPDATE: newer versions use tt-input instead of tt-query */
.tt-hint {
    width: 396px;
    height: 30px;
    padding: 8px 12px;
    font-size: 24px;
    line-height: 30px;
    border: 2px solid #ccc;
    border-radius: 8px;
    outline: none;
}

.tt-query { /* UPDATE: newer versions use tt-input instead of tt-query */
    box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
}

.tt-hint {
    color: #999;
}

.tt-menu { /* UPDATE: newer versions use tt-menu instead of tt-dropdown-menu */
    width: 422px;
    margin-top: 12px;
    padding: 8px 0;
    background-color: #fff;
    border: 1px solid #ccc;
    border: 1px solid rgba(0, 0, 0, 0.2);
    border-radius: 8px;
    box-shadow: 0 5px 10px rgba(0,0,0,.2);
}

.tt-suggestion {
    padding: 3px 20px;
    font-size: 18px;
    line-height: 24px;
    cursor: pointer;
}

.tt-suggestion:hover {
    color: #f0f0f0;
    background-color: #0097cf;
}

.tt-suggestion p {
    margin: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://twitter.github.io/typeahead.js/releases/latest/typeahead.bundle.js"></script>

<input class="typeahead" type="text" placeholder="where ?">
ali karimi
  • 542
  • 4
  • 13
1

Nowadays you'd just use prefetch:

const bloodhound = new Bloodhound({
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    prefetch: '/jsonarrayendpoint',
    datumTokenizer: Bloodhound.tokenizers.whitespace
});

$('#search').typeahead({
    highlight: true
},
{
    source: bloodhound
});

If your results are just an array of strings, the above is all you need. If they're objects you have more work to do though. Assuming data that has Id .id and text to display .name:

const bloodhound = new Bloodhound({
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    prefetch: '/jsonresultsendpoint',

    identify: d => d.id,
    datumTokenizer: d => Bloodhound.tokenizers.whitespace(d.name)
});

$('#search').typeahead({
    highlight: true
},
{
    source: bloodhound,
    display: 'name',
});
Chris Moschini
  • 36,764
  • 19
  • 160
  • 190
1

I don't have a working example for you nor do I have a very clean solution, but let me tell you what I've found.

If you look at the javascript code for TypeAhead it looks like this:

items = $.grep(this.source, function (item) {
    if (that.matcher(item)) return item
  })

This code uses the jQuery "grep" method to match an element in the source array. I didn't see any places you could hook in an AJAX call, so there's not a "clean" solution to this.

However, one somewhat hacky way that you can do this is to take advantage of the way the grep method works in jQuery. The first argument to grep is the source array and the second argument is a function that is used to match the source array (notice Bootstrap calls the "matcher" you provided when you initialized it). What you could do is set the source to a dummy one-element array and define the matcher as a function with an AJAX call in it. That way, it will run the AJAX call just once (since your source array only has one element in it).

This solution is not only hacky, but it will suffer from performance issues since the TypeAhead code is designed to do a lookup on every key press (AJAX calls should really only happen on every few keystrokes or after a certain amount of idle time). My advice is to give it a try, but stick with either a different autocomplete library or only use this for non-AJAX situations if you run into any problems.

Aamer Abbas
  • 106
  • 1
  • 3
-1

when using ajax, try $.getJSON() instead of $.get() if you have trouble with the correct display of the results.

In my case i got only the first character of every result when i used $.get(), although i used json_encode() server-side.

larsbo
  • 119
  • 1
  • 6
-2
 $('#runnerquery').typeahead({
        source: function (query, result) {
            $.ajax({
                url: "db.php",
                data: 'query=' + query,            
                dataType: "json",
                type: "POST",
                success: function (data) {
                    result($.map(data, function (item) {
                        return item;
                    }));
                }
            });
        },
        updater: function (item) {
        //selectedState = map[item].stateCode;

       // Here u can obtain the selected suggestion from the list


        alert(item);
            }

    }); 

 //Db.php file
<?php       
$keyword = strval($_POST['query']);
$search_param = "{$keyword}%";
$conn =new mysqli('localhost', 'root', '' , 'TableName');

$sql = $conn->prepare("SELECT * FROM TableName WHERE name LIKE ?");
$sql->bind_param("s",$search_param);            
$sql->execute();
$result = $sql->get_result();
if ($result->num_rows > 0) {
    while($row = $result->fetch_assoc()) {
    $Resut[] = $row["name"];
    }
    echo json_encode($Result);
}
$conn->close();

?>

moahmed ayed
  • 636
  • 7
  • 10