2

Let's say I have the following code:

var Klass = function(){
  var self = this;
  this.value = 123;

  this.update = function(){
    $.ajax({
      url: '/whatever', 
      async: false,
      success: function(data){
        $.extend(self, data);
      }
    });
  }
}

Lets assume, '/whatever' returns this json object:

{value: 234}

And when I do this:

var obj = new Klass();
obj = ko.mapping.fromJS(obj);
console.log(obj);

We all know obj is now an knockoutjs observable.

And I run this:

obj.update();
console.log(obj);

What I have discovered is, value of obj doesn't get overridden as a simple value 234, but stayed as an observable property.

My questions are:

1) why is this?

2) How do I make the update work as I wanted.

UPDATE: ajax call is not asynchronous.

PhearOfRayne
  • 4,762
  • 3
  • 28
  • 44
James Lin
  • 20,109
  • 28
  • 106
  • 192

1 Answers1

0

First issue is that you are extending self, which is a scoped variable and only exists inside the Klass function, not it's instances you create by calling it.

You'll need to call $.extend(this, data); if you need to overwrite value when calling update. Although I do understand why you are using self there. But the observable functionality added by calling ko.mapping.fromJS is then lost. value is no longer a function (ko observable) but a scalar value (234). You have to call obj = ko.mapping.fromJS(obj); again to wrap value as observable.

Second issue is that $.get is asynchronous so calling console.log(obj) right after calling obj.update will log the value before the GET response comes. You need to wait for it to execute (use a callback).

Here's a working fiddle.

var Klass = function(){
  this.value = 123;

  this.update = function(callback){
      var self = this;
      $.get('/', function(data) {
          $.extend(self, {value: 234});
          callback.call(undefined);
      });
  }
}
var obj = new Klass();
obj = ko.mapping.fromJS(obj);
console.log(obj.value());

obj.update(function() {
    obj = ko.mapping.fromJS(obj);
    console.log(obj.value());
});
Community
  • 1
  • 1
Sergiu Paraschiv
  • 9,575
  • 5
  • 33
  • 45
  • Yes I will call mapping again after – James Lin Dec 05 '13 at 08:54
  • Hi regarding the callback, actually I am using asycn:false, I just typed this code up way more simpler here before going to sleep. But thanks for pointing that out. – James Lin Dec 05 '13 at 18:16
  • Also can you please explain a bit more in what `self` is without `var self = this;` inside update function? – James Lin Dec 05 '13 at 18:29
  • 1
    Check this [http://jsfiddle.net/7RrKd/3/]. What you'll find is that the first `console.log(this)` is a reference to `Klass`, the other is a reference to `obj`, and this is very important. This [http://stackoverflow.com/questions/1646698/what-is-the-new-keyword-in-javascript#answer-3658673] answer explains why. It's because of `new` and what it does. `Klass` is just a prototype, a _constructor_ if you will, while `obj` is an instance. Inside the constructor `this` is a reference to the constructor itself. Inside a function call of an instance (`obj`), it's referring to itself. – Sergiu Paraschiv Dec 05 '13 at 19:34