Search code examples
javascriptajaxjsonknockout.jsknockout-mapping-plugin

Updating a knockout observable after an async ajax call using ko.mapping.fromJS


I have a simple use case. I need to call a WS asynchronously and show the returned JSON on the UI. The JSON that I get back is an object with multiple properties. For simplicity, the code below only has one property. Since I have multiple properties, I am using ko.mapping.fromJS to map the JSON to the object properties. It all seems to work, except the fetched data doesn't update on the UI. If I manually update the observable, it works. But not when using ko.mapping.fromJS.

Javascript

function AppViewModel() {
var self = this;
self.firstName = ko.observable("Bert");

$.ajax({
    dataType: 'json',
    async: true,
    type: 'POST',
    url: '/echo/json/',
    data: {
        json: '{"firstName":"Bob1"}'
    }
}).done(function(data) {
    console.log(data);

    //self.firstName(data.firstName);//This works
    self = ko.mapping.fromJS(data); //This doesn't

    console.log(self.firstName());
}).fail(function(jqxhr, textStatus, error) {
    alert('there was an error');
});
}

// Activates knockout.js
var avm = new AppViewModel();
ko.applyBindings(avm);

HTML

<p>First name: <strong data-bind="text: firstName"></strong></p>

You can run the jsfiddle. You will see that this line works

self.firstName(data.firstName);//This works

and this line doesn't work

self = ko.mapping.fromJS(data); //This doesn't

http://jsfiddle.net/texag93/fakdf5Lw/53/


Solution

  • Two things: 1) You need to create your initial view model with ko.mapping.fromJS, and 2) you need to pass your existing view model as a second parameter to fromJS when updating it.

    So something like this instead:

    // Activates knockout.js
    var avm = ko.mapping.fromJS({firstName: 'Bert'});
    ko.applyBindings(avm);
    
    $.ajax({
        dataType: 'json',
        async: true,
        type: 'POST',
        url: '/echo/json/',
        data: {
            json: '{"firstName":"Bob1"}'
        }
    }).done(function(data) {
        console.log(data);
    
        ko.mapping.fromJS(data, avm);
    
        console.log(avm.firstName());
    }).fail(function(jqxhr, textStatus, error) {
        alert('there was an error');
    });
    

    Updated fiddle: http://jsfiddle.net/fakdf5Lw/56/