1

I have the following code inside a backbone view:

var BookView = Backbone.View.extend({

initialize: function() {
    this.render();
},

render: function() {

  this.model.fetch({
    success : function(model, resp, opt) {
       alert(this.$el.html() ); //THIS ONE DOESN'T WORK?
    }
  });
  alert(this.$el.html() ); // THIS ONE WORKS FINE
}

});

I have two alert(this.$el.html() ); calls, one outside the fetch, and one inside. But for some reason, the one outside the fetch call works, but the one inside the fetch call returns an error: Uncaught TypeError: Cannot read property 'html' of undefined

Muhambi
  • 3,472
  • 6
  • 31
  • 55

4 Answers4

5

Inside success, this is no longer the View (it is undefined in strict mode, or window otherwise).

To fix this, you can use the common var that = this idiom; For more info on the idiom, see here: What does 'var that = this;' mean in JavaScript?

var BookView = Backbone.View.extend({

initialize: function() {
    this.render();
},

render: function() {
  var that = this; // define here

  this.model.fetch({
    success : function(model, resp, opt) {
       alert(that.$el.html() ); // Use that instead of this here.
    }
  });
  alert(this.$el.html() ); // THIS ONE WORKS FINE
}

});

Alternate option: See .bind() - A decent recourse: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

render: function() {
  this.model.fetch({
    success : function(model, resp, opt) {
       alert(that.$el.html() ); 
    }.bind(this) // this.model.fetch.success now has bound 'this' permanently for any call to success from this method form here on out. 
  });
  alert(this.$el.html() );
}
Community
  • 1
  • 1
Matt
  • 74,352
  • 26
  • 153
  • 180
  • Since this was accepted can you please add to your answer the reference to .bind AND remove the OP comments. Give any readers the opportunity to figure out which is best for their scenario. – Eric Hodonsky Jul 16 '14 at 17:07
  • @relic: on the road atm. I've CW'd my post, so feel free to make the changes yourself, or I'll do it soon! – Matt Jul 16 '14 at 17:10
  • Hey cool, nice on the road answer :+1: – Eric Hodonsky Jul 16 '14 at 17:16
2

Remember that this inside a JavaScript function depends on how the function is called rather than how or where it is defined (except for bound functions of course). The Model#fetch documentation doesn't specify any particular this for the success callback so it is probably being called as a plain function (i.e. this is window inside the success callback). The result is that this.$el is undefined inside your success callback.

You have various options:

  1. Use the standard var _this = this trick to tunnel the desired context into the callback:

    var _this = this;
    this.model.fetch({
      success : function(model, resp, opt) {
        alert(_this.$el.html());
      }
    });
    
  2. Use Function.prototype.bind to use a bound function as the callback:

    this.model.fetch({
      success : function(model, resp, opt) {
        alert(this.$el.html());
      }.bind(this)
    });
    
  3. Use _.bind to bind the callback to the desired this:

    this.model.fetch({
      success : _(function(model, resp, opt) {
        alert(this.$el.html());
      }).bind(this)
    });
    
mu is too short
  • 426,620
  • 70
  • 833
  • 800
0

That's because you don't have access to 'this' anymore, the inside call to 'this' is referencing the callback function actually.

Try binding 'this' to the return function, or setting 'this' to a variable inside the render scope to 'self' or something.

1-

render: function() {
  this.model.fetch({
    success : function(model, resp, opt) {
       alert(this.$el.html() );
    }
  }.bind(this)); //Passes 'this' along for execution on the callback. 
  alert(this.$el.html() );
}

2-

render: function() {
  var self = this;
  this.model.fetch({
    success : function(model, resp, opt) {
       alert(self.$el.html() ); //the scope of 'self' makes it in here
    }
  };
  alert(this.$el.html() );
}
Eric Hodonsky
  • 5,617
  • 4
  • 26
  • 36
0

this in javascript is the current context. So for instance if success() would be called as click event handler from a button, then this would be the button.

Try to inspect what this in the success() method really is. Read a good tutorial about closures, e.g.: http://www.javascriptkit.com/javatutors/closures.shtml

As a quick solution, I recommend this correction:

render: function() {

  var that = this; // <<<<< Change 1

  this.model.fetch({
    success : function(model, resp, opt) {
      //alert(this.$el.html() ); //THIS ONE DOESN'T WORK?
      alert(that.$el.html() ); // <<<<<< Change 2
    }
   });
  alert(this.$el.html() ); // THIS ONE WORKS FINE
}

Should do the trick.

peter_the_oak
  • 3,529
  • 3
  • 23
  • 37