Typeahead.js with Knockout.js

Excellent use of a Knockout.js with Typeahead.js and Bloodhound.js. The observable array can be updated.

by Pranav Kulkarni

HTML

<script src="//cdnjs.cloudflare.com/ajax/libs/knockout/3.0.0/knockout-min.js"></script>
<script src="http://twitter.github.io/typeahead.js/releases/latest/typeahead.bundle.js"></script>
<div>
    <h3>My control</h3>
<input type="text" data-bind="typeahead: { name: 'something', taOptions: theseOptions, displayKey: 'Name' }, value: thisValue" />
</div>

<button data-bind="click: addNew">Add new Option</button>

JavaScript

ko.bindingHandlers.typeahead = {
    init: function (element, valueAccessor) {
        var options = ko.unwrap(valueAccessor()) || {},
            $el = $(element),
            triggerChange = function () {
                $el.change();
            };
            
        var displayKey = options.displayKey;
        options.displayKey = function(item) {
            return item[displayKey]();
        };
        options.dupDetector = function(remoteMatch, localMatch) {
            return false;
        };
        options.source = options.taOptions.ttAdapter();

        console.log('options local set - ', options.taOptions.ttAdapter);
        var thisTypeAhead = $el.typeahead(null, options)
        .on("typeahead:selected", triggerChange)
        .on("typeahead:autocompleted", triggerChange)
        ;
        
        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            $el.typeahead("destroy");
            $el = null;
        });
    }
};

function Option(id, name) {
    var self = this;
    self.Id = ko.observable(id);
    self.Name = ko.observable(name);
}

function viewModel() {
    var self = this;    
    self.thisValue = ko.observable();
    self.someOptions = ko.observableArray([
        new Option(1, 'John'),
        new Option(2, 'Johnny'),
        new Option(3, 'Billy')
    ]);
    self.theseOptions = new Bloodhound({
      datumTokenizer: function(d) { 
          var seomth = Bloodhound.tokenizers.whitespace(d.Name());
          console.log(seomth);
          return seomth },
      queryTokenizer: Bloodhound.tokenizers.whitespace,
        remote : {
            url : '%QUERY',
            transport : function(url, options, onSuccess, onError) {
                var deferred = $.Deferred();
                deferred.done( function() { onSuccess(this); });
                
                var filterVal = url.toLowerCase();
                var result = self.someOptions().filter( function(item) {
                    return...