Reputation: 21275
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.
Upvotes: 1
Views: 700
Reputation: 114792
Your issue is that in your "enable" computeds, you are returning the value
property of item that is found. You would likely want to check if childItems
exists and then if childItems
has a length that is greater than 0.
Otherwise, you could remove the "enable" computeds, and just bind against the length of the options like enable: models().length
(will be falsey if 0 length and truthy if greater than 0).
Here is an updated fiddle: http://jsfiddle.net/rniemeyer/GWVW8/1/
There were also some typos (childItems
vs. childitems
).
Upvotes: 2