Search code examples
knockout.jsgetjson

Knockout.js binding with multiple getJSON requests


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);
    });
});

Solution

  • 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 -->