Accessing Properties of Parent Backbone View

AdamVickers picture AdamVickers · Feb 13, 2012 · Viewed 9.4k times · Source

I have a backbone view that calls to a sub-view:

  lr.MapView = Backbone.View.extend({
    el: $('#map'),
    foo: "bar",
    initialize: function() {
      var that = this;
      _.bindAll(this, "render", "addAllEvents", "addOneEvent");
      this.collection = new lr.Events();
      this.collection.fetch({
        success:  function(resp) {
          that.render();
          that.addAllEvents();
        }
      });   
    },

    addAllEvents: function() {
      this.collection.each(this.addOneEvent);
    },

    addOneEvent: function(e) {
      var ev = new lr.EventView({ 
        model:  e
      });
    },

    render: function() {
    } 
  });

Here is the sub-view:

  lr.EventView = Backbone.View.extend({
    initialize: function() {
      _.bindAll(this, "render");
      console.log(lr.MapView.foo); // will console.log 'undefined' 
    },
    render: function() {
    }
  });

I'd like to be able to access properties the parent view within the sub-view, but it isn't working with the above code. For example, how can I access the 'foo' variable within the sub-view?

Answer

mu is too short picture mu is too short · Feb 13, 2012

lr.MapView is a "class", everything that Backbone.View.extend builds will be in lr.MapView.prototype, not in lr.MapView. Run this with the console open and you'll see whats going on:

var MapView = Backbone.View.extend({ foo: 'bar' });
console.log(MapView);
console.log(MapView.prototype);
console.log(MapView.prototype.foo);

Demo: http://jsfiddle.net/ambiguous/DnvR5/

If you're only going to have a single MapView then you can refer to lr.MapView.prototype.foo everywhere:

initialize: function() {
  _.bindAll(this, "render");
  console.log(lr.MapView.prototype.foo);
}

Note that everywhere includes within lr.MapView instances so your foo will act like a "class variable" from non-prototype based OO languages.

The right way to do this is to use an instance variable for foo and pass the parent view instance to the sub-view instances when they're created:

// In MapView
addOneEvent: function(e) {
  var ev = new lr.EventView({
    model: e,
    parent: this
  });
}

// In EventView
initialize: function(options) {
  _.bindAll(this, "render");
  this.parent = options.parent; // Or use this.options.parent everywhere.
  console.log(this.parent.foo); 
}

Or better, add an accessor method to MapView:

_foo: 'bar',
foo: function() { return this._foo }

and use that method in EventView:

initialize: function(options) {
    // ...
    console.log(this.parent.foo());
}

Proper encapsulation and interfaces are a good idea even in JavaScript.