2
votes

I'm trying to call a Api that returns Json. I'm attempting to put this returned data into a knockout observable array. My view model looks like this:

var adminData = $.getJSON("/api/administrators");
//console.log(adminData);

var viewModel = {
    administrators: ko.observableArray(adminData)
};

ko.applyBindings(viewModel);

The request goes through and an object is being returned with the expected data in adminData, but when I try to add it to ko.observableArray I get this in the console: The argument passed when initializing an observable array must be an array. I can't figure out how to get that data into an array for knockout.

3

3 Answers

10
votes

$.getJSON in asynchronous. The result data is available in the callback only. It is not available as the return value of $.getJSON The documentation never mentions a return value.

$.getJSON("/api/administrators", null, function(adminData, status, xhr){
    var viewModel = {
        administrators: ko.observableArray(adminData)
    };  
    ko.applyBindings(viewModel); 
});

If you need to make separate AJAX calls, you should use jQuery.when See Wait until all jQuery Ajax requests are done?

$.when($.ajax("/api/administrators"), $.ajax("api/roles")).done(function(resp1, resp2){        
    ko.applyBindings({
        administrators: ko.observableArray(resp1[0]),
        roles: ko.observableArray(resp2[0]);
    }); 
});

Here are some other less than ideal solutions, but it shows you what happens under the hood.

If you don't mind that the requests wait for each other

$.getJSON("/api/administrators", null, function(adminData){
    $.getJSON("/api/administrators", null, function(apiRoles){
        ko.applyBindings({
            administrators: ko.observableArray(adminData),
            roles: ko.observableArray(apiRoles);
        }); 
    });
});

If you do care, it's more complicated since you need to track that the requests finished

var ajaxAdminData, ajaxApiRoles
$.getJSON("/api/administrators", null, function(adminData, status, xhr){
    var ajaxAdminData = adminData;
    // If the other call finished, apply the bindings
    if (ajaxApiRoles) {
        applyBindings();
    }
});

$.getJSON("/api/administrators", null, function(apiRoles, status, xhr){
    ajaxApiRoles = apiRoles;
    // If the other call finished, apply the bindings
    if (ajaxAdminData) {
        applyBindings();
    }
});

function applyBindings() {
    ko.applyBindings({
        administrators: ko.observableArray(ajaxAdminData),
        roles: ko.observableArray(ajaxApiRoles);
    }); 
}
1
votes

Because getJSON() is asynchronous! You can not treat it as a synchronous method. Look at what that console.log line is, it would show why it failed.

Use the callback

$.getJSON("/api/administrators", function(adminData) {

    var viewModel = {
        administrators: ko.observableArray(adminData)
    };

    ko.applyBindings(viewModel);
}
0
votes

In your $.getJSON callback just update your observable array then call valueHasMutated() on that array as in: administrators.valueHasMutated()