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,352
  • 6
  • 28
  • 54

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
  • 72,564
  • 26
  • 147
  • 178
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
  • 413,090
  • 67
  • 810
  • 771
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
  • 4,928
  • 4
  • 24
  • 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,353
  • 2
  • 20
  • 35