0

I have a javascript 'class' which contains a wrapper method to call jquery .ajax(). I want to pass in the onSuccess and onError function handlers, but am not sure how. I can do this with plain old global functions, but I'm trying to improve my javascript (from Java background). Any pointers would be appreciated.

In the _makeAjaxCall() method below, how do I reference the onSuccessHandler

function testApp() {
  new X();
}

function X() {
  // Init X by making Ajax call, passing the func to be called on ajax return
  this._makeAjaxCall(initUrl, this.onSuccessInit, this.onError);
  // Make another ajax call to init another component
  this._makeAjaxCall(initUrl, this.onSuccessSomeOtherAjaxCall, this.onError);
}

X.prototype.onSuccessInit = function(){
  this.doStuff(...);
}

X.prototype.onSuccessSomeOtherAjaxCall = function(){
  this.doOtherStuff(...);
}


/**
 * make an ajax call, and call the provided success/error handler
 */
X.prototype._makeAjaxCall = function(url, onSuccessHandler, onError){
  $.ajax({
    url : url,    
    success : function (jsonData, textStatus, XMLHttpRequest) {
      // If I don't user 'this', the func called but I've lost my reference
      // to my instance of X
      onSuccessHandler();

      // If  I use 'this', it points to the ajax call object, not to my X object.
      this.onSuccessHandler();

    }
  });
}
Kevin
  • 11,181
  • 22
  • 78
  • 97

3 Answers3

1

The problem is that when the success callback is called by the $.ajax function, the default context is used window. You need to tell JQuery that you want a different context, so you can do one of 3 things:

Add a context attribute to the hash that is sent to $.ajax, so I your case you can do:

$.ajax({
   url: url,
   context: this, // this will tell JQuery to use the right context
   success: this.onSuccessHandler
});

Use JQuery's $.proxy function, like:

$.ajax({
    url: url,
    success: $.proxy(this.onSuccessHandler, this) // this will bind the correct context to the callback function
});

Cache the variable this, like @mVChr suggested, although I would encourage you to use self as it has become somewhat of a javascript idiom

 var self = this;
 $.ajax({
     url: url,
     success: function(data) {
         self.onSuccessHandler(data);
     }
 });

Edit:

If you need a more in depth explanation of context and scope in javascript checkout this article: http://www.digital-web.com/articles/scope_in_javascript/

CarlosZ
  • 7,011
  • 1
  • 19
  • 16
  • Hi - thanks for the response - solution #1 doesn't work, I get Object # has no method 'onSuccessHandler', solution #2 fails silently and solution #3 doesn't work for me because I have multiple success handlers I pass in to the ajax wrapper. I have updated the question to clarify the way I need to be able to call the ajax wrapper method - it is used from multiple methods and I have multiple success handler methods. Perhaps this isn't doable in javascript and I need to refactor... – Kevin Mar 28 '11 at 20:56
  • It worked for me by just adding the `context: this` attribute to the `$.ajax`, see call http://jsfiddle.net/bX35E/ BTW, in `_makeAjaxCall` you are calling `.ajax` instead of `$.ajax`. – CarlosZ Mar 28 '11 at 21:16
  • Hi - still not it because I don't have the context of my instance in the success handlers. I've updated your script : http://jsfiddle.net/bX35E/2/ - See the instance variable is undefined when the success handlers are called. – Kevin Mar 29 '11 at 10:59
  • Fron de jsfiddle, you don't really need this construct return function() { alert("onSuccessSomeOtherAjaxCall, _instanceVariable="+self._instanceVariable); } You can access the `this` variable in the handlers directly since it will be set correctly by the call to `$.ajax` with `context: this` – CarlosZ Mar 29 '11 at 15:36
0

Cache this within the local scope of _makeAjaxCall before conducting the ajax call:

X.prototype._makeAjaxCall = function(url, onSuccessHandler, onError){
  var _X = this; // cache this

  $.ajax({
    url : url,    
    success : function (jsonData, textStatus, XMLHttpRequest) {
      // use cached this
      _X.onSuccessHandler();
    }
  });
}
mVChr
  • 46,510
  • 10
  • 101
  • 99
0

Thanks to input from CarlosZ & mVChr, I've figured out the solution, http://jsfiddle.net/bX35E/3/

$(document).ready(function testApp() {
  new X();
});

function X() {
  console.dir(this);
  var initUrl = "/echo/json/";
  this._instanceVariable = "I AM defined!";
  // Init X by making Ajax call, passing the func to be called on ajax return
  this._makeAjaxCall(initUrl, this.onSuccessInit(), this.onError);
  // Make another ajax call to init another component
  this._makeAjaxCall(initUrl, this.onSuccessSomeOtherAjaxCall(), this.onError);
}

X.prototype.onSuccessInit = function(){
  //this.doStuff(...);
    var self = this;
    return function() {
       alert("onSuccessInit, _instanceVariable="+self._instanceVariable);
    }
}

X.prototype.onSuccessSomeOtherAjaxCall = function(){
    var self = this;
    return function() {
      alert("onSuccessSomeOtherAjaxCall, _instanceVariable="+self._instanceVariable);
    }    
}
Kevin
  • 11,181
  • 22
  • 78
  • 97