I've built a cascading dropdown with Knockout, but the cascading part (ko.computed
) is not updating.
Knockout version is 3.0.0
.
Preface:
I'm using a tree data structure to model the cascading.
{ id: '', text: '', childItems: [] }
Idea taken from:
knockout.js - nested array data and cascading pre-populated dropdown lists binding
Html:
<select data-bind="options: manufacturers,
optionsCaption:'Manufacturer',
optionsText: 'text',
optionsValue: 'id',
value: selectedManufacturer">
</select>
<select data-bind="options: models,
optionsCaption:'Model',
optionsText: 'text',
optionsValue: 'id',
value: selectedModel,
enable: enableModels">
</select>
<select data-bind="options: engines,
optionsCaption:'Engine',
optionsText: 'text',
optionsValue: 'id',
value: selectedEngine,
enable: enableEngines">
</select>
JS:
ViewModel:
function ViewModel(items) {
this.manufacturers = ko.observableArray(items);
// These three observables should be numbers (e.g. 1)
// Corresponding to the id
this.selectedManufacturer = ko.observable();
this.selectedModel = ko.observable();
this.selectedEngine = ko.observable();
function getById(items, id) {
return ko.utils.arrayFirst(items, function(item) {
return item.id === id;
});
}
this.models = ko.computed(function(){
var items = ko.utils.unwrapObservable(this.manufacturers);
var id = ko.utils.unwrapObservable(this.selectedManufacturer);
return id ? getById(items, id).childItems : [];
}, this);
this.enableModels = ko.computed(function(){
var items = ko.utils.unwrapObservable(this.manufacturers);
var id = ko.utils.unwrapObservable(this.selectedManufacturer);
return id ? getById(items, id).value > 0 : false;
}, this);
// generate engines based on models
this.engines = ko.computed(function(){
var items = ko.utils.unwrapObservable(this.models);
var id = ko.utils.unwrapObservable(this.selectedModel);
return id ? getById(items, id).childItems : [];
}, this);
this.enableEngines = ko.computed(function(){
var items = ko.utils.unwrapObservable(this.models);
var id = ko.utils.unwrapObservable(this.selectedModel);
return id ? getById(items, id).value > 0 : false;
}, this);
}
Data:
var items = [
{ text: 'Ford', id: 1, childItems:
[
{ text: 'F-150', id: 1, childitems:
[
{ text: 'Gasoline', id: 1, childitems: [] },
{ text: 'Diesel', id: 2, childitems: [] }
]
},
{ text: 'F-250', id: 2, childitems:
[
{ text: 'Gasoline', id: 3, childitems: [] },
{ text: 'Diesel', id: 4, childitems: [] }
]
}
]
},
{ text: 'Honda', id: 2, childItems:
[
{ text: 'Civic', id: 5, childitems:
[
{ text: 'Gasoline', id: 5, childitems: [] },
{ text: 'Electric', id: 6, childitems: [] }
]
},
{ text: 'Accord', id: 6, childitems:
[
{ text: 'Gasoline', id: 7, childitems: [] },
{ text: 'Electric', id: 8, childitems: [] }
]
}
]
}
];
Binding:
var module = {};
module.viewModel = new ViewModel(items);
ko.applyBindings(module.viewModel);
Update:
Here is the complete working sample based on the answer.