Twitter Bootstrap TypeAhead + Knockout.js

A small example of how to databind observableArrays, normal arrays or even object properties to your typeahead.

HTML

<script src="http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.1.0/bootstrap.min.js"></script>
<script src="http://ajax.aspnetcdn.com/ajax/knockout/knockout-2.1.0.js"></script>
<h1> Here's three examples of ways to add datasource to Twitter Bootstraps Typeahead. </h1>
<div class="container">
    <p>Color</p>
    <input type="text" data-bind="typeahead: colors"/>
    <br/>
    <p>Tasts</p>
    <input type="text" data-bind="typeahead: 'mySuggestions'"/>
    <br/>
    <p>Animals</p>
    <input type="text" data-bind="typeahead: 'myComplexObject.animals'"/>
    <br/>
    
</div>

CSS

@import url('http://twitter.github.com/bootstrap/assets/css/bootstrap.css');

.container {
    margin-top: 10px;
    margin-left: 30px;
}

JavaScript

var ViewModel = function() {
    this.colors = ko.observableArray(["Red", "Green", "Gray", "Golden", "Yellow", "Black", "Blue"]);
}

var mySuggestions = ["Sweet", "Sour", "Salt"]; // this may for example be from ajax call...
var myComplexObject = {
    animals: ["pig", "pony", "dog", "cat", "cow", "chicken", "fish", "fox"]
}; // some sort of object that you're using.
//here's the binding
ko.bindingHandlers.typeahead = {
    init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var typeaheadSource; // <-- this is where our typeahead options will be stored in
        //this is the parameter that you pass from you data-bind expression in the mark-up
        var passedValueFromMarkup = ko.utils.unwrapObservable(valueAccessor());
        if (passedValueFromMarkup instanceof Array) typeaheadSource = passedValueFromMarkup;
        else {
            // if the name contains '.', then we expect it to be a property in an object such as myLists.listOfCards
            var splitedName = passedValueFromMarkup.split('.');
            var result = window[splitedName[0]];
            $.each($(splitedName).slice(1, splitedName.length), function(iteration, name) {
                result = result[name];
            });

            // if we find any array in the JsVariable, then use that as source, otherwise init without any specific source and hope that it is defined from attributes
            if (result != null && result.length > 0) {
                typeaheadSource = result;
            }

        }
        if (typeaheadSource == null) $(element).typeahead();
        else {
            $(element).typeahead({
                source: typeaheadSource
            });
        }

    },
};
ko.applyBindings(new ViewModel());