Cascading Dropdowns Select

cascading dropdowns with Select2 and Knockout

by Doug Hill

HTML

<link rel="stylesheet" href="https://rawgithub.com/ivaynberg/select2/master/select2.css">
<script src="https://rawgithub.com/ivaynberg/select2/master/select2.js"></script>
<script src="http://ajax.aspnetcdn.com/ajax/knockout/knockout-3.0.0.js"></script>
<div>
    <select id="make" data-bind="options: carMakers, value: selectedMake.id, optionsValue: 'text', optionsText : 'text', optionsCaption : 'Select your make', select2: {}"></select><br/>
    Selected Make: <span data-bind="text: selectedMake().text"></span><br/>
    <select id="type" data-bind="options: carTypes, value: selectedType.id, optionsValue: 'text', optionsText : 'text', optionsCaption : 'Select your type', enable : carTypes, select2: {}"></select><br/>
    Selected Model: <span data-bind="text: selectedType().text"></span><br/>
    <select id="model" data-bind="options: carModels, value: selectedModel.id, optionsValue: 'text', optionsText : 'text', optionsCaption : 'Select your Model', enable: carModels, select2: {}"></select><br/>
    Selected Model: <span data-bind="text: selectedModel().text"></span><br/>
</div>

CSS

.select2-container
{
    width:150px;
}

select
{
    width:150px;
}

JavaScript

//ko binding handler
ko.bindingHandlers.select2 = {
    init: function(element, valueAccessor) {
        $(element).select2(valueAccessor());

        ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
            $(element).select2('destroy');
        });
    },
    update: function(element) {
        $(element).trigger('change');
    }
};

//Define our options model
var cascadingOption = function(data){
    var self = this;
    self.text = data.text;
    self.childOptions = data.childOptions;
}

//fill our models with example data
function buildData(){
    var fordTrucks = new cascadingOption({
        text: 'Trucks',
        childOptions : [
            new cascadingOption({
                text: 'F150'
            }),
            new cascadingOption({
                text: 'SuperDuty'
            })
        ]
    });

    var fordCars = new cascadingOption({
        text: 'Cars',
        childOptions : [
            new cascadingOption({
                text: 'Focus'
            }),
            new cascadingOption({
                text: 'Mustang'
            })
        ]
    });

    var fords = new cascadingOption({
        text: 'Ford',
        childOptions : [fordTrucks, fordCars]
    });

    var audis = new cascadingOption({
        text: 'Audi',
        childOptions : [
            new cascadingOption({
                text:'Crossovers',
                childOptions : [
                    {text: 'Q5'},
                    {text: 'Q7'}
                ]
            }),
            new cascadingOption({
                text:'Cars',
                childOptions : [
                    {text: 'A3'},
                    {text: 'A4'},
                    {text: 'A6'}
                ]
            })
        ]
    });

    return [fords, audis];
}

var makeObservableForSelect2 = function( sourceOptions, idSelector ) {
    var target = ko.observable({});
    target.id = ko.observable();
    
    target.id.subscribe( function(id) {
        var...