Getting started with knockout, I have been playing with the pattern found at http://knockoutjs.com/examples/cartEditor.html. I have cascading select menus where the second one's options depend on the state of the first -- no problem so far. But whatever I do, I haven't figured a way to change the out-of-the-box behavior whereby the second element is not visible -- not rendered, I would imagine -- until the first element has a true-ish value (except by taking out the optionsCaption
and instead stuffing in an empty record at the top of my data -- more on that below.) The markup:
<div id="test" class="border">
<div class="form-row form-group">
<label class="col-form-label col-md-3 text-right pr-2">
language
</label>
<div class="col-md-9">
<select class="form-control" name="language"
data-bind="options: roster,
optionsText: 'language',
optionsCaption: '',
value: language">
</select>
</div>
</div>
<div class="form-row form-group">
<label class="col-form-label col-md-3 text-right pr-2">
interpreter
</label>
<div class="col-md-9" data-bind="with: language">
<select class="form-control" name="interpreter"
data-bind="options: interpreters,
optionsText : 'name',
optionsCaption: '',
value: $parent.interpreter"
</select>
</div>
</div>
</div>
Code:
function Thing() {
var self = this;
self.language = ko.observable();
self.interpreter = ko.observable();
self.language.subscribe(function() {
self.interpreter(undefined);
});
};
ko.applyBindings(new Thing());
my sample data:
roster = [
{ "language": "Spanish",
"interpreters": [
{"name" : "Paula"},
{"name" : "David"},
{"name" : "Humberto"},
{"name" : "Erika"},
{"name" : "Francisco"},
]
},
{"language":"Russian",
"interpreters":[{"name":"Yana"},{"name":"Boris"}]
},
{"language":"Foochow",
"interpreters":[{"name":"Lily"},{"name":"Patsy"}]
},
/* etc */
Now, I did figure out that I can hack around this and get the desired effect by putting
{ "language":"", "interpreters":[] }
at the front of my roster
data structure, and that's what I guess I will do unless one of you cognoscenti can show me the more elegant way that I am overlooking.
After using both Knockout and Vuejs, I found Vuejs much easier to work with. Knockout is a bit out dated and no longer supported by any one or group.
Having said that, here is how I addressed your issue. The comments here refer to the link you provided not your code so I could build my own test case.
My working sample is at http://jsbin.com/gediwal/edit?js,console,output
I added the disable:isDisabled to the second selectbox cause I want it to be disabled when nothing is selected in the first selectbox.
added self.isDisabled = ko.observable(true); to the cartline model
altered the subscription to check the new value. If it is the select option the second one gets lock.
function formatCurrency(value) {
return "$" + value.toFixed(2);
}
var CartLine = function() {
var self = this;
// added this to enable/disable second select
self.isDisabled = ko.observable(true);
self.category = ko.observable();
self.product = ko.observable();
self.quantity = ko.observable(1);
self.subtotal = ko.computed(function() {
return self.product() ? self.product().price * parseInt("0" + self.quantity(), 10) : 0;
});
// Whenever the category changes, reset the product selection
// added the val argument. Its the new value whenever category lchanges.
self.category.subscribe(function(val) {
self.product(undefined);
// check to see if it should be disabled or not.
self.isDisabled("Select..." == val.name);
});
};
var Cart = function() {
// Stores an array of lines, and from these, can work out the grandTotal
var self = this;
self.lines = ko.observableArray([new CartLine()]); // Put one line in by default
self.grandTotal = ko.computed(function() {
var total = 0;
$.each(self.lines(), function() { total += this.subtotal() })
return total;
});
// Operations
self.addLine = function() { self.lines.push(new CartLine()) };
self.removeLine = function(line) { self.lines.remove(line) };
self.save = function() {
var dataToSave = $.map(self.lines(), function(line) {
return line.product() ? {
productName: line.product().name,
quantity: line.quantity()
} : undefined
});
alert("Could now send this to server: " + JSON.stringify(dataToSave));
};
};