George Reith
George Reith

Reputation: 13476

Backbone View property returning undefined

Here is my view:

define(
    [
        "jquery"
    ,   "underscore"    
    ,   "backbone"
    ,   "eventView"
    ]
,   function($, _, Backbone, EventView) {
        "use strict";

        var TimelineView = Backbone.View.extend({
            tagName: 'div'
        ,   className: 'column'

        ,   _EventViews: {} // Cache event views for reuse

        ,   initialize: function() {
                this.collection.bind('add', this.add);
                this.collection.bind('reset', this.add);
            }

        ,   render: function() {
                return this;
            }

            // Listen for additions to collection and draw views
        ,   add: function(model) {
                var eventView = new EventView({
                    model: model
                });

                // Cache the event
                console.log(this._EventViews);
                this._EventViews[model.get('id')] = eventView;

                // Draw event
                eventView.render(); 
            }
        });

        return TimelineView
    }
);

As you can see I set the _EventViews property to contain an empty object. However when I call the add() function console.log(this._EventViews) returns undefined and the following statement fails.

Can anyone tell me why this is?

Upvotes: 0

Views: 1246

Answers (2)

Paul Hoenecke
Paul Hoenecke

Reputation: 5060

The problem is that within add, this is not your TimelineView. See this article for an explanation of context in javascript: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/this

You can solve this in a few different ways. The easiest in this situation is to use the third parameter of bind or on (these two are the same).

initialize: function() {
            this.collection.on('add', this.add, this);
            this.collection.on('reset', this.add, this);
        }

Or use listenTo instead.

initialize: function() {
            this.listenTo(this.collection, 'add', this.add);
            this.listenTo(this.collection, 'reset', this.add);
        }

Also, your _EventViews cache will be shared by all instances of TimelineView. If that is not what you want, create it in initialize instead.

initialize: function() {
            this._EventViews = {};
            this.listenTo(this.collection, 'add', this.add);
            this.listenTo(this.collection, 'reset', this.add);
        }

Upvotes: 2

A. Matías Quezada
A. Matías Quezada

Reputation: 1906

It works for me:

<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://underscorejs.org/underscore-min.js"></script>
<script src="http://backbonejs.org/backbone.js"></script>
<script>

var TimelineView = Backbone.View.extend({
    tagName: 'div'
,   className: 'column'

,   _EventViews: {} // Cache event views for reuse

,   initialize: function() {
        //this.collection.bind('add', this.add);
        //this.collection.bind('reset', this.add);
    }

,   render: function() {
        return this;
    }

    // Listen for additions to collection and draw views
,   add: function(model) {
        var eventView = ({
            model: model
        });

        // Cache the event
        console.log(this._EventViews); // Prints: Object {}
        this._EventViews[model.get('id')] = eventView;

        // Draw event
        eventView.render();
    }
});

var a = new TimelineView();
a.add();

</script>

I think the problem is the .add() method is invoked from the collection add event. When you add a listener (with in backbone is done with the .bind() function) you must bind (on the native meaning) the function:

_.bindAll(this, 'add');

OR

this.add = this.add.bind(this);

You have to do it before you add the function as a listener:

initialize: function() {
    _.bindAll(this, 'add');
    this.collection.bind('add', this.add);
    this.collection.bind('reset', this.add);
}

Upvotes: 1

Related Questions