jQuery UI autocomplete with a Knockout binding handler

(Posted)

by w1ndig0

HTML

<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1/themes/ui-lightness/jquery-ui.css">
<script src="http://cloud.github.com/downloads/SteveSanderson/knockout/knockout-2.1.0.js"></script>
<button data-bind="click: updateDataSource">Update Data Source</button>
<label for="name-search">Search language:</label>
<input type="text" id="name-search" data-bind="value: langName, 
ko_autocomplete: { minLength : 0, source: languages, select: addLang }" />
<div style="margin-top:1em">The observable array contains the following elements:
    <ul data-bind="foreach: selectedLangs">
        <li data-bind="text: $data" />
    </ul>
</div>

JavaScript

ko.bindingHandlers.ko_autocomplete = {
    init: function (element, params) {
        //We make a shallow copy of the object because we will change it
        var conf = $.extend({}, params());

        // This fixes the problem of showing the value instead of the label when pressing up or down
        if (!conf.focus) {
            //There is no focus callback defined so we will add one
            conf.focus = function (event, ui) {
                $(element).val(ui.item.label);
                return false;
            }
        }

        // Use this to check if the autocomplete is already initialize
        var isAutoComplete = $(element).is(':data(autocomplete)');

        // Use an observable instead of a fixed array
        var source = conf.source; // <- assuming an observable here                            
        conf.source = source(); // get the actual content        
        $(element).autocomplete(conf);      
                
        if (!isAutoComplete) {
            //watch for modifications
            var subscription = source.subscribe(function (newValue) {
                $(element).autocomplete("option", "source", newValue);
            });                                                
            
            //handle disposal                                    
            ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
                subscription.dispose();
                $(element).autocomplete("destroy");
            });
        }
    },
    update: function (element, params) {
        $(element).autocomplete("option", "source", params.source);
        return false;
    }
}

$(function () {

    var availableTags = [{
        label: "ActionScript",
        value: 1
    }, {
        label: "AppleScript",
        value: 2
    }, {
        label: "Asp",
        value: 3
    }, {
        label: "BASIC",
        value: 4
    }, {
        label: "C",
        value: 5
    }, {
        label: "C++",
        value: 6
  ...