Cascading Dropdown using knockout.

2,180 views
Skip to first unread message

Ek

unread,
Feb 24, 2012, 11:14:04 PM2/24/12
to knock...@googlegroups.com
I am trying to make a cascading drop-down using knockout. but I am not able to filter the array based on the parent selection. Here is the code. Please help if you have any idea. 

@{  
    Layout = "~/_SiteLayout.cshtml";
    Page.Title = "Welcome to my Web Site!";
}
<script src="Scripts/knockout-2.0.0.js" type="text/javascript"></script>
<script src="Scripts/jquery-1.7.1.min.js" type="text/javascript"></script>

<table>
    <tr>
        <td>Country</td>
        <td>
            <select 
                data-bind="options: countries, optionsText: 'countryName', value: selectedCountry, optionsCaption: 'Choose...'">
            </select>
        </td>
    </tr>
    <tr>
        <td>State</td>
        <td>
            <select 
                data-bind="options: selectedStates, optionsText: 'stateName', optionsValue: 'stateName', value: selectedState, optionsCaption: 'Choose...'">
            </select>
        </td>
    </tr>
</table>
 
<script type="text/javascript">
    var country = function (countryId, countryName) {
        this.countryId = ko.observable(countryId); 
        this.countryName = ko.observable(countryName);
    };
    var state = function (stateId, stateName, countryId) {
        this.stateId = ko.observable(stateId);
        this.stateName = ko.observable(stateName);
        this.countryId = ko.observable(countryId);
    };

    var viewModel = {
        countries: ko.observableArray([
                    new country(1, "USA"),
                    new country(2, "Germany"),
                    new country(3, "India")
                ]),
        selectedCountry: ko.observable(), // Nothing selected by default

        states: ko.observableArray([
                    new state(101, "California", 1),
                    new state(201, "Berlin", 2),
                    new state(301, "Kerala", 3)
                ]),
        selectedState: ko.observable()// Nothing selected by default
    };

    viewModel.selectedStates = ko.dependentObservable(function () {
        return this.states();
    }, viewModel);
    ko.applyBindings(viewModel);

</script>

rpn

unread,
Feb 25, 2012, 12:52:55 AM2/25/12
to knock...@googlegroups.com
Here is an updated version of your fiddle that does filtering in the computed observable:  http://jsfiddle.net/rniemeyer/gWYVK/1/ 

Ek

unread,
Feb 25, 2012, 12:45:20 PM2/25/12
to knock...@googlegroups.com
Thank you very much rpn, its been a very good help.

I was trying to get one more layer for city. but not working. would you please help to resolve this issue.
Here is the code
----------------------------------------------------------------------------------
<table>
    <tr>
        <td>
            Country
        </td>
        <td>
            <select data-bind="options: countries, optionsText: 'countryName', value: selectedCountry, optionsCaption: 'Choose...'">
            </select>
        </td>
    </tr>
    <tr>
        <td>
            State
        </td>
        <td>
            <select data-bind="options: selectedStates, optionsText: 'stateName', value: selectedState, optionsCaption: 'Choose...'">
            </select>
        </td>
    </tr>
    <tr>
        <td>
            City
        </td>
        <td>
            <select data-bind="options: selectedCities, optionsText: 'cityName', value: selectedCity, optionsCaption: 'Choose...'">
            </select>
        </td>
    </tr>
</table>

<script type="text/javascript">
    var country = function (countryId, countryName) {
        this.countryId = ko.observable(countryId);
        this.countryName = ko.observable(countryName);
    };
    var state = function (stateId, stateName, countryId) {
        this.stateId = ko.observable(stateId);
        this.stateName = ko.observable(stateName);
        this.countryId = ko.observable(countryId);
    };
    var city = function (cityId, cityName, stateId) {
        this.cityId = cityId;
        this.cityName = cityName;
        this.stateId = stateId;
    };

    var viewModel = {
        countries: ko.observableArray([
                new country(1, "USA"),
                new country(2, "Germany"), 
                new country(3, "India")
            ]),
        selectedCountry: ko.observable(), // Nothing selected by default

        states: ko.observableArray([
                new state(101, "California", 1),
                new state(102, "Florida", 1),
                new state(201, "Bavaria", 2),
                new state(202, "Berlin", 2),
                new state(302, "Karnataka", 3),
                new state(303, "Kerala", 3)
            ]),
        selectedState: ko.observable(),// Nothing selected by default

        cities: ko.observableArray([
                new city(1011, "Fremont", 101),
                new city(1026, "Tallahassee", 102),
                new city(1032, "Atlanta", 103),
                new city(1043, "Raleigh", 104),
                new city(1051, "Houston", 105)
            ]),
        selectedCity: ko.observable() // Nothing selected by default
    };

    viewModel.selectedStates = ko.dependentObservable(function () {
        var country = this.selectedCountry(), countryId;
        if (country) {
            countryId = country.countryId();
            return ko.utils.arrayFilter(this.states(), function (state) {
                return state.countryId() === countryId;
            });
        }
        return [];
    }, viewModel);

    viewModel.selectedCities = ko.dependentObservable(function () {
        var state = this.selectedState(), stateId;
        if (state) {
            stateId = state.stateId();
            return ko.utils.arrayFilter(this.cities(), function (city) {
                return city.stateId() === stateId;
            });
        }
        return [];
    }, viewModel);

    ko.applyBindings(viewModel);

</script>
---------------------------------------------------------------------------------- 

JohnEarles

unread,
Feb 25, 2012, 1:01:36 PM2/25/12
to KnockoutJS
In:

viewModel.selectedCities = ko.dependentObservable(function () {
var state = this.selectedState(), stateId;
if (state) {
stateId = state.stateId();
return ko.utils.arrayFilter(this.cities(), function (city)
{
return city.stateId() === stateId;
});
}
return [];
}, viewModel);

Change: 'return city.stateId() === stateId;'
To: 'return city.stateId === stateId;'

Your city object does not use observables, so you don't need to
address the properties as functions. Alternatively, change city to
use ko.observable just like state and country do.

Ek

unread,
Feb 25, 2012, 1:06:17 PM2/25/12
to knock...@googlegroups.com
Thanks. It worked.

would you please explain me why it worked for the above function
ex: return state.countryId() === countryId; this worked and 
'return city.stateId() === stateId;'  - this code dont work.

also what the operater === is for?

Thank you,
Eldhose 

JohnEarles

unread,
Feb 25, 2012, 4:05:39 PM2/25/12
to KnockoutJS
state.countryId was declared like this:

var state = function (stateId, stateName, countryId) {
...
this.countryId = ko.observable(countryId);
};

city.stateId was declared like this:

var city = function (cityId, cityName, stateId) {
...
this.stateId = stateId;
};

The ko.observable function creates an observable object, but for
compatibility reasons it creates a function. Read the "Reading and
writing observables" section in the Observables documentation for an
explanation:

http://knockoutjs.com/documentation/observables.html

--

The === operator in JavaScript is an "strict" comparison, comparing
both value and type.
The == operator in JavaScript will perform coercions before checking
equality.

If x = 5:

x == 5 is true
x == '5' is true
x === '5' is false

2 == '2' is true, but 2 === '2' is false

Ek

unread,
Feb 25, 2012, 6:12:37 PM2/25/12
to knock...@googlegroups.com
Great explanation! Thank you very much John :-)

Regards,
Eldhose
Reply all
Reply to author
Forward
0 new messages