My sample is working, but I am confused about the location of the ko.applyBindings() statement. I used this approach to populate my ViewModel from a single getJSON request. But suppose I need 2 getJSON requests. I moved the "var viewModel = new MyViewModel();" outside of the getJSON, but the ko.applyBinding() was in both getJSON methods, and I understand you should NOT have 2 bindings to the same VM. I tried moving the ko.applyBinding() below the getJSON, but nothing worked. So I left the ko.applyBinding() inside one of the getJSON methods, and called VM method to set the variable from the other JSON call. It seems to work, but I am concerned if there is a timing issue that may cause issues if the JSON requests return at different times.
var MyViewModel = function() {
var self = this;
self.types = ko.observableArray();
self.states = ko.observableArray();
self.loadStates = function (states){
self.states = states;
}
}
var viewModel = new MyViewModel();
$(function () {
$.getJSON('json/typeArray.json', function(jTypes){
viewModel.types = jTypes;
ko.applyBindings(viewModel);
});
$.getJSON('json/stateArray.json', function(jStates){
viewModel.loadStates(jStates);
//ko.applyBindings(viewModel);
});
});
I could use nested JSON requests, but I would prefer them to execute at the same time.
Why can't the ko.applyBindings(viewModel) be moved to the bottom of this script? I tried, but neither of my arrays get populated.
Update: Yes, there is a timing problem. Sometimes the 2nd "states" array gets updated in the UI, and sometimes it does not. It evidently depends on which getJSON returns first. So I do need to find a solution to this problem.
Here is the attempt to move the applyBindings after viewModel creation, which did not work (see comment):
var MyViewModel = function() {
var self = this;
self.name = "myViewModel";
self.states = ko.observableArray();
self.types = ko.observableArray();
self.loadStates = function (states){
self.states = states;
console.log("Set states in viewModel: " + self.states);
}
}
var viewModel = new MyViewModel();
ko.applyBindings(viewModel);
$(function () {
$.getJSON('json/typeArray.json', function(jTypes){
console.log("Setting types in viewModel: " + viewModel.name);
viewModel.types = jTypes;
//ko.applyBindings(viewModel);
});
$.getJSON('json/stateArray.json', function(jStates){
console.log("Setting states in viewModel: " + viewModel.name);
viewModel.loadStates(jStates);
//ko.applyBindings(viewModel);
});
});
The thing is that you set new values to observable arrays, and do not assign new object to bound properties.
Instead of assigning:
viewModel.types = jTypes;
I propose to use updating:
//viewModel.types(jTypes);
viewModel.types(["type a", 'type b', 'type c']);
I've created a sample (request is emulated via setTimeout), on startup arrays are empty, "times" are updated in 1 second, "states" are updated in 2 seconds:
var MyViewModel = function() {
var self = this;
self.name = "myViewModel";
self.states = ko.observableArray();
self.types = ko.observableArray();
}
var viewModel = new MyViewModel();
ko.applyBindings(viewModel);
//$(function () {
// $.getJSON('json/typeArray.json', function(jTypes){
// viewModel.types(jTypes);
// });
// $.getJSON('json/stateArray.json', function(jStates){
// viewModel.states(jStates);
// });
//});
//$.getJSON('json/typeArray.json', function(jTypes){
setTimeout(function() {
viewModel.types(["type a", 'type b', 'type c'])
}, 1000);
//$.getJSON('json/stateArray.json', function(jStates){
setTimeout(function() {
viewModel.states(["state d", 'state e', 'state f'])
}, 2000);
// ever more - update types again in 5 sec
//$.getJSON('json/typeArray.json', function(jTypes){
setTimeout(function() {
viewModel.types(["type g", 'type h', 'type i', 'type j'])
}, 5000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div>States:</div>
<!-- ko if: states().length === 0 -->
<div>There are no states for a while...</div>
<!-- /ko -->
<!-- ko foreach: states -->
<div data-bind="text: $data"></div>
<!-- /ko -->
<div>Types:</div>
<!-- ko if: types().length === 0 -->
<div>There are no types for a while...</div>
<!-- /ko -->
<!-- ko foreach: types -->
<div data-bind="text: $data"></div>
<!-- /ko -->