Reputation: 2415
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
Upvotes: 3
Views: 1570
Reputation: 2260
I know is an old question but there is now another possibility, using es7 decorators, similar to what angular2 does:
@component({
events: {
"click #testDiv" : "start"
}
})
class TestView extends Backbone.View{
start(){
console.log("Clicked");
}
render(){
// ...
}
}
Decorator file
export function component(definition: any) {
return function (constructor: any) {
Object.assign(constructor.prototype, definition)
}
}
This way, all properties defined in the component decorator will be attached to the class prototype and will be available at instantiation time.
Another option besides using a class/constructor decorator could be to use a method decorator on the preinitialize
method if you wish to have all the code inside the class braces like this:
class TestView extends Backbone.View {
@component({
events: {
"click #testDiv" : "start"
}
})
preinitialize(){}
start(){
console.log("Clicked");
}
render(){
// ...
}
}
Decorator file
export function component(definition: any) {
return function (target: Object, methodName: string, descriptor: TypedPropertyDescriptor<Function>) {
Object.assign(target, definition)
}
}
Upvotes: 1
Reputation: 4090
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();
Upvotes: 2