Can't extend backbone.events in coffeescript
Asked Answered
C

3

15

I'm getting the error:

Uncaught TypeError: Cannot read property 'constructor' of undefined 

When declaring the following class:

 class ViewHelpers extends Backbone.Events

I can use the same syntax to extend Backbone.Router, Views, Model etc. Here is the compiled javascript which I wrote in a quick log to make sure Backbone.Events was there

__t('views').ViewHelpers = (function(_super) {

 #how i know it is definied here
 console.log(_super.trigger)

 __extends(ViewHelpers, _super);

 function ViewHelpers() {
   return ViewHelpers.__super__.constructor.apply(this, arguments);
 }

 return ViewHelpers;

})(Backbone.Events);

So the line causing the error is

ViewHelpers.__super__.constructor.apply(this, arguments);

What is different about __extends() method that it would work for Backbone.View and not Backbone.Events?

Chrysanthemum answered 17/6, 2012 at 0:34 Comment(2)
I've commented on the difference between Backbone.Events and other Backbone objects in an answer below, but, i'd like to ask: what's the purpose of ViewHelpers? Because its name is not the typical name for classes (i.e. singular nouns) so it might be the case that a class is not what you need.Cytherea
Great answer below thanks! I agree helpers are generally defined at a global scope. In this case I maintain a library of functions in a class ViewHelpers, instantiated as a property of my main controller class. There are some functions (for example, CSS transitionEnd handlers) which I may want to subscribe multiple listeners instead of passing a single callback. Since I'm already using Backbone I thought I'd subclass the Event "class" (object) for that purpose. Sorry to be late on this reply, not sure why I didn't get notified months ago.Chrysanthemum
C
38

That's because Backbone.Events is not a "class", so it cannot be extended, it's a "module" that can be mixed-in into other objects (see docs here). In JavaScript terms that means that it's not a Function, that can be called as a constructor (i.e. new Backbone.Events will throw an error), it's just a plain JS object whose properties (methods) can be assigned to other objects to make them event dispatchers.

In CoffeeScript, you can mix-in the Backbone.Events into your objects when they are created:

class ViewHelpers
  constructor: ->
    _.extend @, Backbone.Events

Or you can just extend the class' prototype and avoid having those methods as (own) properties of all ViewHelpers instances:

class ViewHelpers
  _.extend @prototype, Backbone.Events

These two approaches should work and let you instantiate and use ViewHelpers as event dispatchers:

vh = new ViewHelpers
vh.on 'foo', -> alert 'bar'
vh.trigger 'foo'​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​
Cytherea answered 17/6, 2012 at 1:16 Comment(4)
_(@::).extend Backbone.Events is an alternative to _.extend @prototype, Backbone.Events that might read better (depending on the local definition of better of course).Maricruzmaridel
@muistooshort Yeap, and the polar opposite all-the-way-explicit alternative to @:: is, in this case, ViewHelpers.prototype (for anyone wondering =D).Cytherea
Very helpful post, a lot of insight into how Coffee and Backbone work for such a short answer!Isreal
Note that ViewHelpers is being extended (clobbered) by Backbone.Events, so don't expect to be able to define your own on, off, or trigger methods (e.g. to wrap the methods of Backbone.Events with the intention of invoking them via super)Superfuse
S
1

There's another way (from what @epidemian answered), which doesn't involve copying Backbone.Events into a new object to use as your prototype - instead, use Object.create to create a new object to use as your prototype, using Backbone.Events as its prototype.

class ViewHelpers
  @prototype = Object.create(Backbone.Events)

Now ViewHelpers' prototype is a new, empty object whose prototype is Backbone.Events. You can define methods on ViewHelpers' prototype without affecting Backbone.Events, but all the Backbone.Events methods are still available to ViewHelpers, without having to copy them into a new object. This not only saves (a miniscule amount of) memory, but if you ended up adding on to Backbone.Events later, all ViewHelperss would see the change.

For this, you'll need either a browser that has ES5's Object.create function, or an Object.create polyfill.

Sylph answered 10/1, 2014 at 6:23 Comment(0)
S
0

To build on the excellent answer by @epidemian I would add this, it's a bit of a hack but it allows you to write your class with the extends statement as you specified in the question (which lets you call super on all the Backbone.Events methods):

class Events
_.extend Events.prototype, Backbone.Events

class CustomEvents extends Events

    trigger: (event, etc...) ->
        # You can add overrides before
        super "custom:#{event}", etc...
        # or after the super class methods

It would be neat to work the _.extend call into the Events.constructor function but I couldn't make it work...

Superfuse answered 2/9, 2015 at 16:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.