Albert Gao Albert Gao - 3 months ago 42
TypeScript Question

How can I bind an eventhandler to a class method in typescript?

I write an app using typescript. And compile it to js, it works well except for the event binding part. _stateService is a class I injected into this class.

The following class is try to create a DOM element using jQuery, then pass it to another class to render, the creating part works well and it renders finally on the page. But the event binding is somehow tricky.

At first, I want to use $().click() to bind, but failed since the element hasn't been rendered yet.

Then I try to use the $().on() to bind, it works. but at runtime, it throws an error:


TemplatingService.ts:37 Uncaught TypeError: Cannot read property
'CallBack' of undefined(anonymous function) @
TemplatingService.ts:37dispatch @ jquery.min.js:3q.handle @
jquery.min.js:3


It seems that if I just use console.log("test") inside that bind function, it works. But if I try to bind it to a class method, it failed. Seems lost context, how Can I fix this?

The original typescript is:

export class TemplatingService implements ITemplatingService {
_stateService: IStateService;

constructor(stateService: IStateService) {
this._stateService = stateService;
}

createPage(page:IPage):JQuery{
let outerDiv = this.createLayout();
for (let element of page.rawLayout){
switch(element.type){
case "button":
let temp = this.createJQueryButton();
$(".emulator").on('click', "#"+element.name, function(){
this._stateService.CallBack(element);
});
outerDiv.append(temp);
break;
}
}
return outerDiv;
}
}


The related generating js is like the following:

"use strict";
var TemplatingService = (function () {
function TemplatingService(stateService) {
this._stateService = stateService;
}

TemplatingService.prototype.createPage = function (page) {
var outerDiv = this.createLayout();
var _loop_1 = function(element1) {
switch (element.type) {
case "button":
var temp = this_1.createjQueryButton();
$(".emulator").on('click', "#" + element.name, function () {
this._stateService.CallBack(element);
});
outerDiv.append(temp);
break;
}
};
var this_1 = this;
for (var _a = 0, _b = page.rawLayout; _a < _b.length; _a++) {
var element1 = _b[_a];
_loop_1(element1);
}
return outerDiv;

}());

exports.TemplatingService = TemplatingService;
//# sourceMappingURL=TemplatingService.js.map

Answer

you need to bind this for callback.

 $(".emulator").on('click', "#"+element.name, function(){
     this._stateService.CallBack(element);
 }.bind(this));

or a better solution is to save this to a variable

createPage(page:IPage):JQuery{
    const self = this;
    let outerDiv = this.createLayout();
    for (let element of page.rawLayout){
        switch(element.type){
            case "button":
                let temp = this.createJQueryButton();
                $(".emulator").on('click', "#"+element.name, function(){
                     self._stateService.CallBack(element);
                });
                outerDiv.append(temp);
                break;
        }
    }
    return outerDiv;
}