idbentley
idbentley

Reputation: 4218

backbone.js view inheritance. `this` resolution in parent

I have a case that uses view inheritance, and my code looks essentially like:

parentView = Backbone.View.extend({
    events: {
        "some event": "business"
    },
    initialize: function(){
        _.bindAll(this);
    },
    business: function(e){
        ...
        this.someFunc && this.someFunc();
        ...
     }
});

childView = parentView.extend({
    events: {
        ...
    },
    constructor: function(){
       this.events = _.extend( {}, parentView.prototype.events, this.events );
       parentView.prototype.initialize.apply( this );
    },
    initialize: function(){
       _.bindAll(this);
    },
    someFunc: function(){
       ...
    }
});

Update: Moved this.events extension to the constructor.

My child view has someFunc in it, and during some business function in the parent view, it should call that function if it exists. If this is properly set to the childView, then this.someFunc should exist. This, however, is not the behaviour that I am experiencing.

During the initialize function (in the parent), this is indeed set to the child view. However, when some event fires, the business function is called with this set to parentView.

Upvotes: 7

Views: 4567

Answers (3)

Pier Paolo Ramon
Pier Paolo Ramon

Reputation: 2900

Actually, I'dont know if this solves your case, but I usually do this: this.constructor.__super__.initialize.apply(this, arguments); and works like a charm. My solution is completely wrong. Here's why:

var Model1 = Backbone.Model.extend({
  method: function () {
    // does somehting cool with `this`
  }
});

var Model2 = Model1.extend({
  method: function () {
    this.constructor.__super__.method.call(this);
  }
});

var Model3 = Model2.extend({
  method: function () {
    this.constructor.__super__.method.call(this);
  }
});

var tester = new Model3();

// Boom! Say hallo to my little stack-overflowing recursive __super__ call!
tester.method();

The call to this.constructor.__super__ in Model2::method will resolve to (drum-roll) Model2::method.

Always use ExplicitClassName.__super__.methodName.call(this, arg1, arg2 /*...*/) or Coffee-script's super.

Upvotes: 2

Paul
Paul

Reputation: 2157

Have you tried extending this.events in the constructor, instead of in the initialize function? If you do this in initialize, you're too late; event delegation for the business function has already been setup in the constructor, and will point to parentView (see the call to this.delegateEvents(); in Backbone.View's constructor).

Updated with a working example:

ParentView = Backbone.View.extend({
    name: 'ParentView',
    events: {
        "event": "business"
    },
    business: function(e){
        this.someFunc && this.someFunc();
    }
});

ChildView = ParentView.extend({
    name: 'ChildView',
    events: {
    },
    constructor: function(){
       this.events = _.extend( {}, ParentView.prototype.events, this.events );
       console.debug( this.events );
       ParentView.prototype.constructor.apply( this, arguments );
    },
    someFunc: function(){
        console.debug('someFunc; this.name=%s', this.name);
    }
});

child = new ChildView();
$( child.el ).trigger('event');
// logs 'this' in 'someFunc'; the name is 'ChildView'.

Upvotes: 9

c3rin
c3rin

Reputation: 1917

You can solve this problem by adding this line to the initialize method of the child:

_.bind(this.business, this)

Hopefully someone can point you to a better description of the underlying mechanisms than I can provide but I'll give it a shot:

What happens is that the method will use the context of the scope it was defined in unless told otherwise. initialize is told to use the context of the child when you call parentView.prototype.initialize.apply(this) because you are passing in the childView with the this reference to the apply method.

You can bind the business method to the context of the child by using the underscore.js bind method as described above.

Upvotes: 0

Related Questions