1

I am having an "this" issue and would appreciate any help

this is my basic code

function xxx(val)
{
   this.x = val;
   this.change = function() {
     var self = this;
     $.ajax({
         blah: '',
         blah: '',
         success: function(data) { self.x = 5; },
     });
   };
}

var newX = new x(1);
newX.change();

console.log(newX.x);

Hopefully that makes sense,

What I am trying to do is update the original object on the jquery ajax response. I do not have access to 'this' so i tried to pass it off to the 'self' variable but the object values does not seem to be updating even though the code is running as expected.

I am sure there is a simple answer, i just dont know it.

Any help is greatly appreciated.

Fabrício Matté
  • 69,329
  • 26
  • 129
  • 166
QBM5
  • 2,778
  • 2
  • 17
  • 24
  • Side-note: you probably meant `newX = new xxx(1)`; On topic: `$.ajax` is **ASYNCHRONOUS** - you can easily tell by the callback function - while the rest of your code has a synchronous execution model. Of course this will never work, you need to work with callbacks/Deferreds/Promises. – Fabrício Matté Feb 26 '13 at 21:18
  • How is `change` called? This is probably the cause of your problem as Evan has suggested below. If `change` is being called by an event handler, for example, then `this` in `change` is probably not what you think it is. – Matt Burland Feb 26 '13 at 21:22
  • @MattBurland The line `newX.change();` seems more than clear for me. – Fabrício Matté Feb 26 '13 at 21:24
  • @FabrícioMatté: You are correct, but I'm betting that's not how it's actually called. However, your point about the call being asynchronous is also an issue that means that even the test code doesn't do what the OP probably expects. – Matt Burland Feb 26 '13 at 21:26
  • possible duplicate of [How to return the response from an AJAX call from a function?](http://stackoverflow.com/questions/14220321/how-to-return-the-response-from-an-ajax-call-from-a-function) – Fabrício Matté Feb 26 '13 at 21:29

2 Answers2

3

So the way to resolve this is take a look at your function declarations. Each function declaration will give you a new this object. Store the one you are interested in the correct spot. If I'm correct, it looks like you actually want to access the scope of the original xxx function scope. So instead of storing this in the change function, store it above that in the original scope. Something like this:

function xxx(val)
{
   var self = this;

   this.x = val;

   this.change = function() {
     var that = this;
     $.ajax({
         blah: '',
         blah: '',
         success: function(data) { self.x = 5; },
     });
   };
}

var newX = new x(1);
newX.change();

console.log(newX.x);

The other issue here is that you are using AJAX to make that call so you either need a Deferred object or you could add a callback to that function that gets triggered at the right time like so:

   function xxx(val)
        {
           var self = this;

           this.x = val;

           this.change = function(callback) {
             var that = this;
             $.ajax({
                 blah: '',
                 blah: '',
                 success: function(data) { 
                      self.x = 5;
                      if (typeof callback === "function"){
                            callback.call(this);
                      }
                 }
             });
           };
        }


    var newX = new xxx(1);

    newX.change(function(){
        console.log(newX.x);
    });
Evan
  • 5,975
  • 8
  • 34
  • 63
2

In knockout... you'll have to do something like that:

function Xxx(val)
{
   var self = this;

   this.x = ko.observable(val);

   this.change = function() {
     // "that" may be different to "self" in some
     // cases...
     var that = this;
     $.ajax({
         url: '...',
         type: 'get',
         success: function(data) {
             self.x(5);
         },
         error: function(a) {
             console.log('got an error');
         }
     });
   };
}

var newX = new Xxx(1);
newX.change();

ko.computed(function () {
    // This will get called everytime
    // newX.x is changed
    console.log(newX.x());
});

When you create a variable, that may change, you have to create it as an observable. The observable is in fact a function that you call. When called, it will update its inner value and it will also trigger any changes wherever the observable is "observed"

In no way you should try to do this.x = 5. It will override the actual observable object and thus it will never trigger every observer of a change.

edit

In case you're interested to understand how does computed works. A computed variable is a function that will listen to observables. When the computed is created, it will be called once to check which observables where called from within it. It's a way to "track" dependencies. In this example, you should see at least two console log. one with 1, and then with 5.

In my case, the computed variable is kind of anonymous since it isn't affected anywhere. Also in some case, you may need to observe one variable but use multiple observables. To prevent update on any other used observables. There are some ways to do that. You can either return after you "watched" the observables you needed.

Or you can create a sub function that will be triggered a little after the computed with setTimeout(..., 0);. There are a couple of ways to achieve some really nice tricks.

Loïc Faure-Lacroix
  • 13,220
  • 6
  • 67
  • 99
  • Thanks for your input. I was using ko.mapping.fromJS to turn the object observable after it was constructed. I just made all the values ko.obserables and it worked like a charm. Thanks so much – QBM5 Feb 26 '13 at 22:03
  • ko.mapping.fromJS should by default make observables. And it's probably better to use the mapping plugin than doing it all by yourself. – Loïc Faure-Lacroix Feb 26 '13 at 22:13