Search code examples
backbone.jstypescript

Running BackboneJs events in Typescript


I'm writing a Backbone program in Typescript, in which I am not able to initialize any events. Here's a test class that I've created to fix the problem. The function start() is not being called when the div is clicked on.

class TestView extends Backbone.View{

    events = {
        "click #testDiv" : "start"
    }

    start(){
        console.log("Clicked");
    }

    constructor(options?){
        super(options);
    }

    render(){
        $root.html(getNewDiv("testDiv"));
        $("#testDiv").css("height", 100).css("width", 100).css("background-color", "green");
        console.log("Rendered");
        return this;
    }
}

function getNewDiv(id:string) {
    return "<div id = \"" + id + "\"></div>"
}

new TestView().render();

Here's the console output:

Rendered

Here's the backbone typescript definition that I'm using:

https://github.com/borisyankov/DefinitelyTyped/blob/master/backbone/backbone.d.ts


Here's the CDN location for backboneJS

Minified : http://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.0/backbone-min.js

Non-Minified : http://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.0/backbone.js

I'm concerned if my syntax is right or this has something to do with the Typescript definition of Backbone.

UPDATE

The answer shown by Kevin Peel below is throwing an error, because in the Typescript definition file, "events" is defined as a function ( eg. events()). If I just create an "events" function, I get the error - "Uncaught TypeError: Object [object Object] has no method 'off'". If I use the getter method (eg. get events(){}), then nothing really happens on actually firing the event.

Using delegate Events

I tried to use the delegateEvents() function in Backbone.View which creates an error:

constructor(options:any, question:Question, div:JQuery) {
        this.delegateEvents({
           "click" : "clicked"
        });
    }

Error:

Uncaught TypeError: Object [object Object] has no method 'off' backbone.js:1082
h.extend.undelegateEvents backbone.js:1082
h.extend.delegateEvents backbone.js:1059

Solution

  • The problem is with where TypeScript defines the events. When you define events like this:

    class TestView extends Backbone.View {
        events = {
            "click #testDiv": "start"
        }
        // ...
    }
    

    What TypeScript does is attaches events as a property after initializing the instance. So when Backbone is initializing the view, events haven't yet been attached, so Backbone isn't able to bind them.

    Unfortunately, there doesn't seem to be a nice way to get TypeScript to assign events to the prototype, so you need to hack around it in some way.

    The absolute easiest way is by setting the events on the prototype yourself:

    class TestView extends Backbone.View {
        // Get rid of the events declaration in here
        // ...
    }
    
    TestView.prototype.events = {
        "click #testDiv": "start"
    }
    

    A second possible way to get around the issue is using the getter functionality of TypeScript as suggested in this StackOverflow answer.

    Finally, a third way would be to bind your events each time you render. This is another easy way to get around the TypeScript problem, but it might not be the best solution if the view gets rendered many times.

    class TestView extends Backbone.View{
        events = {
            "click #testDiv" : "start"
        }
    
        // ...
    
        render(){
            $root.html(getNewDiv("testDiv"));
            // Here is where you'll bind the events using Backbone's delegateEvents
            this.delegateEvents();
            $("#testDiv").css("height", 100).css("width", 100).css("background-color", "green");
            console.log("Rendered");
            return this;
        }
    }
    
    // ...
    
    new TestView().render();