Reputation: 2390
I know it has been asked before, but it I've been at it for hours and I couldn't find a viable solution. Summarising, I receive an JSON string from the Server (C#), like this:
function CallFromServer() {
$.ajax({
type: "POST",
async: false,
url: "Default.aspx/GetTest",
data: "{ }",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (msg) {
locationsList = jQuery.parseJSON(msg.d);
}
});
};
Pretty vanilla, right? Each entry of locationsList consists of a string, which is a state and another list, consisting of the cities in this given state. I'm pretty new to KnockoutJS (and it's not even my project, I'm just doing a small freelance), so after messing around for a while, I came to this conclusion on how to populate comboboxes:
HTML:
<select id="stateSelect" onchange="GetCities()" style="background-color:#69bed2; width:50px;" data-placeholder="..."
data-bind="options: stateValue" name="state" class="chosen-select" tabindex="1">
</select>
<select id="citySelect" style="background-color:#69bed2; width:143px;" data-placeholder="Select a state"
data-bind="options: cityValue" name="city" class="chosen-select" tabindex="2">
</select>
JS:
function CalculatorViewModel()
{
var self = this;
CallFromServer(); //Mine
GetStates(); //Mine
self.percentBonus = ko.observable(3);
self.typedValue = ko.observable("0");
self.bonusValue = ko.observable('');
self.messageValue = ko.observable("");
self.stateValue = ko.observableArray(stateArray); //Mine
self.cityValue = ko.observableArray(cityArray); //Mine
(...) //A lot of stuff
}
What isn't commented as "Mine" was pretty much there already and along with some reading of the KOJS documentation, I used it in order to try to achieve what I wanted.
Ok, now let's get to the gist of it. One might notice that I used
onchange="GetCities()
in the first select. Here's it's code:
function GetCities() {
cityArray.clear();
cityArray[0] = '';
var currentState = $('#stateSelect :selected').text();
$.each(locationsList, function (i, e) {
if (e.stateName == currentState) {
$.each(e.cityList, function (j, city) {
cityArray[j] = e.cityList[j].cityName;
});
}
});
};
As espected, whenever this onChange event is raised when a state is changed, self.cityArray updates itself to the proper value. However, the combobox to which it is bound won't refresh, no matter what. I've read a lot about different ways to do it, but I feel that I'm so close with this approach that starting from scratch is not an option.
Does anyone have any ideas of what I'm missing here?
BTW, don't worry about my stateArray binding, it works just fine. RENDERING the new set of options provided by the cityArray after selecting a state is what is proving to be troublesome.
Upvotes: 0
Views: 541
Reputation: 63760
I think you may be confused as to how Knockout works, it may help going through the turorials. You seem to be doing two things that go against the idea of KO that are part of your problem:
onchange
event like that, but use some Knockout construct (for example a subscription) for that;cityArray
instead of the cityValue
observable array; the UI will only respond to changes in the latterFurthermore, you'll need:
selectedOptions
binding to bind an observable to the selected state(s)Some changes I'd at least suggest, in respective order:
<select id="stateSelect"
style="background-color:#69bed2; width:50px;"
data-placeholder="..."
data-bind="options: stateValue, selectedOptions: selectedStates"
name="state"
class="chosen-select"
tabindex="1">
</select>
The view model needs to be extended with something to hold the selection of states:
function CalculatorViewModel()
{
// Added:
self.selectedStates = ko.observableArray([]);;
}
Also remove the onchange
direct event wiring. Replace it by something along these lines, subscribing to the selectedOptions:
// Use a KO subscription:
self.selectedStates .subscribe(function(newStateValue) {
// Clear the *observable* array
self.cityValues.removeAll();
$.each(locationsList, function (i, e) {
if (e.stateName == newStateValue) {
$.each(e.cityList, function (j, city) {
// Push into the observable array so the UI gets updated
self.cityValues.push(e.cityList[j].cityName);
});
}
});
});
Here's a fiddle to demo how this works.
Upvotes: 2