Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Backbone.js listenTo window resize throwing [object Object] has no method 'apply' error

Tags:

Problem:

I'm trying to attach a resize event to the window from a view using the new listenTo() method in Backbone.js. The event seems to bind to the window, however, when the window is actually resied the following error is thrown:

Uncaught TypeError: Object [object Object] has no method 'apply' jquery.js:2 p.event.dispatch jquery.js:2 p.event.add.g.handle.h

Here is the code that attaches the event to the view:

this.listenTo($(window),"resize", this.resizeContext, this));

Here is the resizeContext function:

  resizeContext: function(event) {

            console.log("resizing context for "+this.id);

            this.setHeight();

            // trigger resize event (use event bus)
            this.options.vent.trigger("resize", event);

        }

Note: using the standard $(window).on("resize",this.resizeContext) attaches the event and runs as it should. I am trying to take advantage of the new stopListening() feature that is added to view.remove();

like image 657
Zengineer Avatar asked Jan 22 '13 14:01

Zengineer


3 Answers

The new listenTo and stopListening are methods of the Backbone.Events mixin, and they can only be used to listen to Backbone events which are triggered with .trigger, such as the built-in collection:add, or model:change events.

That means that you won't be able to utilize the stopListening functionality for DOM events such as window:resize.

Consider overriding the View.removemethod instead.

var SomeView = Backbone.View.extend({
  initialize:function() {
    $(window).on("resize",this.resizeContext)
  },

  remove: function() {
    $(window).off("resize",this.resizeContext);
    //call the superclass remove method
    Backbone.View.prototype.remove.apply(this, arguments);
  }
});
like image 61
jevakallio Avatar answered Nov 12 '22 15:11

jevakallio


If you want to keep using listenTo you might want to use following one off wrapper for DOM elements:

/**
 * Use Backbone Events listenTo/stopListening with any DOM element
 *
 * @param {DOM Element}
 * @return {Backbone Events style object}
 **/
function asEvents(el) {
    var args;
    return {
        on: function(event, handler) {
            if (args) throw new Error("this is one off wrapper");
            el.addEventListener(event, handler, false);
            args = [event, handler];
        },
        off: function() {
            el.removeEventListener.apply(el, args);
        }

    };
}

Example:

view.listenTo(asEvents(window), "resize", handler);

and the listener will be remove automatically on view.remove() or view.stoplistening()

Here's more complex implementation for multiple event listeners https://gist.github.com/epeli/5927950

like image 26
Epeli Avatar answered Nov 12 '22 13:11

Epeli


In my code, I need to do .debounce((this.resizeContext).bind(this)).

Which makes it harder to be turned off. As a dirty solution, I just turn off all 'resize' listener when remove the view. I guess in the new view, if there is any resize listener, it will be turned on again.

remove: function() {
    $(window).off("resize");
    //call the superclass remove method
    Backbone.View.prototype.remove.apply(this, arguments);
}
like image 23
sszhupku Avatar answered Nov 12 '22 14:11

sszhupku