twitter bootstrap typeahead ajax example
L

17

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.

Loper answered 10/2, 2012 at 18:20 Comment(2)
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.Loper
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.Backbencher
F
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.

Floozy answered 23/8, 2012 at 18:27 Comment(13)
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?Yorgo
As in the Typeahead fork, data must be a JSON array of strings and the content type must be application/json.Floozy
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?Lenna
@Anton: you could do the processing in the anonymous function before calling process(...)Floozy
@Stijin Are there any examples of how that anonymous function would be used here, to process an id for a displayed option? Thanks!Archeozoic
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.Approximate
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); }); } } }); Approximate
The one thing that tripped me up was leaving the data-provide="typeahead" attribute in the <input> element. The AJAX call won't be made with that attribute in place. I forgot to remove it after testing a more simple example.Congou
why use get instead of getJSON ? It seems more appropriate.Abbacy
@StijnVanBael ,Excuse me ... as you said bootsrap 3no longer supports this so by your idea whats better for doing such thing in web?Melton
Using typeahead.js would be the way to goFloozy
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);}); }Eighth
Update on my comment: I've been using Bootstrap-3-Typeahead for this.Eighth
I
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);
        });
    }
});
Iata answered 24/2, 2012 at 17:39 Comment(9)
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().Pouncey
@rfausak Alternatively, set the Content-type header to application/jsonPouncey
i am getting Uncaught TypeError: Cannot call method 'toLowerCase' of undefinedSclar
In the link you sent, I'm not even getting the source function to execute.Volplane
I solved the problem "Cannot call method 'toLowerCase' of undefined" by playing around with the loading order of the js scripts (jquery, bootstrap, typeahead).Dissatisfactory
I think the async call should be $.getJSONGorrono
Nice answer! I would use a GET request instead, just to meet REST standards.Tarkington
hi, I found this solution to be parse an error, and #11179012 solution to be helpfulForetooth
simple example to create smart autocomplete with twitter bootstrap's typeahead. I needed this for a project. Enjoy. gist.github.com/mrgcohen/5062352Pontone
L
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.

Lacylad answered 31/10, 2012 at 11:21 Comment(2)
But I would change it to use $(this)[0].$element.data('link').Polypary
or this.$element.data('link')Pompon
S
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.

Skill answered 20/5, 2015 at 4:49 Comment(5)
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"] }Overanxious
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"}]Skill
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?Fides
MY GOD!!! I was searching from past 3 days no one mentioned about this. I was testing with sync function till now. THANKS DUDE!!Anastasia
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.Thrilling
O
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

Outtalk answered 5/5, 2012 at 23:41 Comment(4)
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! :)Mooring
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.Outtalk
absolutely correct, thank you for the catch! :) This plugin works very well.Mooring
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.1Reniti
J
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.

Jhvh answered 12/2, 2012 at 17:59 Comment(3)
which version of jquery ui min are you using?Loper
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. gist.github.com/1884819 - lines are commented with //modifiedSteatopygia
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! :)Repeat
L
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,
});
Largeminded answered 7/5, 2013 at 19:25 Comment(0)
F
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');
    }
});
Ferruginous answered 22/12, 2014 at 19:14 Comment(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?Morganite
D
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);
        });
    }
});
Dumpcart answered 16/9, 2015 at 22:43 Comment(0)
B
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.

Bonhomie answered 13/2, 2012 at 17:17 Comment(0)
E
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...

Entertaining answered 21/6, 2012 at 21:34 Comment(3)
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 } });Tomikotomkiel
thank you @TomislavMarkovski I modified my code as you suggest.Entertaining
For the latest version, use "display" instead of "property"Villeneuve
B
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
Butternut answered 24/6, 2013 at 23:24 Comment(0)
B
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>
Boiling answered 4/7, 2013 at 21:56 Comment(0)
P
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 ?">
Prader answered 5/5, 2018 at 14:3 Comment(0)
B
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',
});
Biofeedback answered 27/7, 2023 at 0:25 Comment(0)
P
-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.

Pimentel answered 16/5, 2013 at 7:42 Comment(0)
P
-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();

?>

Penang answered 2/3, 2018 at 9:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.