A Small Form and Cascading Drop Down with KnockoutJS

by Avi Algaly

HTML

<script src="http://cloud.github.com/downloads/SteveSanderson/knockout/knockout-2.2.0.debug.js"></script>
<fieldset>
    <div class="field">
        <label for="resident-name">Resident Name</label>
        <input type="text" id="resident-name" data-bind="value: residentName, valueUpdate: 'afterkeydown'" />
    </div>
    <div class="field">
        <label for="street-name">Street</label>
        <input type="text" id="street-name" data-bind="value: streetName, valueUpdate: 'afterkeydown'" />
    </div>
    <div class="field">
        <label for="number-name">Number</label>
        <input type="text" id="number-name"  data-bind="value: numberName, valueUpdate: 'afterkeydown'" />
    </div>
    <div class="field">
        <label for="country-name">CountryName</label>
        <select id="country-name" data-bind="options: availableCountries, optionsText: 'name', optionsCaption: 'Choose Country...', value: countryName"></select>
    </div>
    <div class="field">
        <label for="city-name">City Name</label>
        <select id="city-name" data-bind="options: availableCities, optionsText: 'cityName', optionsCaption: 'Choose City...', value: cityName"></select>
    </div>
    <button id="submit-command" data-bind="click: submitClick, enable: validateModel">Submit</button>
</fieldset>
<br/>
<button id="show-info-command" >Show Info</button>

SCSS

body {
    font-family: Arial, Verdana;
}
fieldset {
    width: 500px;
    padding: 20px;
    background: #f0f0f0;
    overflow:auto;
    
    /* Border style */
    border: 1px solid #cccccc;
    border-radius: 7px;
    box-shadow: 2px 2px 2px #cccccc;
    
    .field {
        float: left;
        clear: both;
    }
    
    label {
        text-shadow: 2px 2px 2px #ccc;
        display: block;
        float: left;       
        width: 120px;
        line-height: 25px;
        font-size: 15px;
    }
    input, select {
        font-size: 15px;
        padding: 5px;
        border: 1px solid #b9bdc1;
        color: #797979;
        float: left;
        margin-bottom: 5px;
        width: 130px;    
    }
    select {
        width: (130 + 10)px;
    }
    button {
        float: right;
        clear: both;        
        margin:10px 55px 10px 0;
        font-weight: bold;
        line-height: 1;
        padding: 6px 10px;
        cursor:pointer;
        color: #fff;
     
        text-align: center;
        text-shadow: 0 -1px 1px #64799e;
     
        /* Background gradient */
        background: #a5b8da;
        background: -moz-linear-gradient(top, #a5b8da 0%, #7089b3 100%);
        background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#a5b8da), to(#7089b3));
     
        /* Border style */
        border: 1px solid #5c6f91;
        border-radius: 10px;
     
        /* Box shadow */
        box-shadow: inset 0 1px 0 0 #aec3e5;        
    
        &[disabled] {
            opacity: 0.5;            
        }
    }
}

JavaScript

var newPropertyViewModel = {
    residentName: ko.observable("John Doe"),
    streetName: ko.observable("Sesame"),
    numberName: ko.observable("5"),
    cityName: ko.observable(""),
    countryName: ko.observable(""),
    availableCountries: ko.observableArray([]),
    submitClick: function() {
        alert("Submitting data...");
    }
};

newPropertyViewModel.availableCities = ko.dependentObservable(function(currentCity) {
    var country = this.countryName();
    var availableCities = {
        cities: []
    };
    if (country) {
        availableCities = ko.utils.arrayFilter(citiesByCountry, function(currentCity) {
            return currentCity.countryId === country.id;
        });
    }
    //if (availableCities.length && availableCities.length > 0) {
    //    availableCities = availableCities[0];
    //}
    return availableCities;
}, newPropertyViewModel);

newPropertyViewModel.validateModel = ko.computed(function() {
    var isValid = (this.residentName() || false) && (this.streetName() || false) && (this.numberName() || false) && (this.cityName() || false) && (this.countryName() || false);

    return isValid;
}, newPropertyViewModel);

ko.applyBindings(newPropertyViewModel);

$("#show-info-command").on("click", function() {
    var currentState = {
        residentName: newPropertyViewModel.residentName(),
        streetName: newPropertyViewModel.streetName(),
        numberName: newPropertyViewModel.numberName(),
        cityName: newPropertyViewModel.cityName(),
        countryName: newPropertyViewModel.countryName()
    };
    alert(JSON.stringify(currentState));
    //alert(ko.utils.toJSON(newPropertyViewModel));
});

// load countries
var countries = [
    {
    id: 1,
    name: "Israel"},
{
    id: 2,
    name: "USA"},
{
    id: 3,
    name: "Canada"}
];

// load cities
var citiesByCountry = [
    {
        "countryId": 0,
        "id": 0,
        "cityName": "choose"
    },
    {
        "countryId": 1,
        "id": 2,
        "cityName":...