0

Consider this simple TS script

class foo {
    v: number = 1;

    public bar() {
        console.log(this.v);
    }
}

var a = new foo();
var b = new foo();

document.getElementById('test').addEventListener("click", a.bar);
document.getElementById('test').addEventListener("click", b.bar);

and the HTML

<html lang="en">
<body>
    <button id="test">Test</button>
</body>
</html>

I would expect to get 2 lines console output of number "1".

But NO! I get one undefined output

Lets take a look at the generated js file

var foo = (function () {
    function foo() {
    this.v = 1;
    }
    foo.prototype.bar = function () {
        console.log(this.v);
    };
    return foo;
}());
var a = new foo();
var b = new foo();
document.getElementById('test').addEventListener("click", a.bar);
document.getElementById('test').addEventListener("click", b.bar);
//# sourceMappingURL=Test.js.map

For performance consideration TS decided to put the function on the prototype! so the addEventListener call was actually adding the prototype (static) function twice and its the same instance. That's why I'm only getting one output instead of two.

And the biggest issue is that the this inside the prototype function refers to the button and button doesn't contain a property called v!

If we do this in native js

var foo = function () {
    this.v = 1;
    var that = this;
    this.bar = function () {
        console.log(that.v);
    }
}

var a = new foo();
var b = new foo();
document.getElementById('test').addEventListener("click", a.bar);
document.getElementById('test').addEventListener("click", b.bar);

we would get the desire result!

Is this a known issue for TS where you can't use the class method as event handler?

And how can I remove that handler after I added it?

Ignacio Ara
  • 2,071
  • 2
  • 22
  • 32
Steve
  • 10,544
  • 6
  • 29
  • 66
  • `document.getElementById('test').addEventListener("click", a.bar);` => `document.getElementById('test').addEventListener("click", a.bar.bind(a));` (added `.bind(a)`). See the linked question's answers for details. – T.J. Crowder Apr 19 '18 at 16:52
  • *"For performance consideration TS decided to put the function on the prototype!"* No, it's on the prototype because that's where you said it should be, by using method syntax within a `class` construct. (It's also the right place to put it.) – T.J. Crowder Apr 19 '18 at 16:53
  • @T.J.Crowder I don't see why this is a duplicate. I clearly stated the fact I know why this is happening and I even posted an example of pure JS that's working. I just want to know how I can tell TS to generate the script so that it behaves the same way as the pure js I posted – Steve Apr 19 '18 at 16:59
  • 1
    SO's definition of a duplicate is that the answers there answer the question. The answers there do so: Use `bind`. Use a property you create in the constructor (or in a property initializer, since you're using TypeScript and you have those) as an arrow function. Use a wrapper function. Etc. – T.J. Crowder Apr 19 '18 at 17:03
  • @T.J.Crowder And how can I get two outputs? Since they both refer to the same function there will be only one listener added. And how can I remove only one of the listener but not both? – Steve Apr 19 '18 at 17:04
  • 1
    Exactly like you always do: Pass the same function into `removeEventListener`. Please do have a thorough read of the higher-rated answers to the linked question. If you use `bind`, that means remembering the bound function. If you use an arrow function created in the constructor (or property initializer), you can just use the function directly. – T.J. Crowder Apr 19 '18 at 17:09
  • @T.J.Crowder you are still missing my point. I added TWO event listeners to the click event. But I am only getting ONE console output. Where as my last example would produce TWO correct ones. .bind will only save me on the `this` part but not the handler part. – Steve Apr 19 '18 at 17:12
  • If you actually tried what the linked question's answers, and the comments above, describe, you'd find that you **do** get two console.logs: One per object. https://jsfiddle.net/avc4dqtw/ – T.J. Crowder Apr 19 '18 at 17:20
  • 1
    and https://jsfiddle.net/avc4dqtw/ (Yikes, the TypeScript feature of jsFiddle gets property initializers or arrow functions wrong, had to transpile this second one via the TS playground.) – T.J. Crowder Apr 19 '18 at 17:26

1 Answers1

0

You can. Just do:

 document.getElementById('test').addEventListener("click", () => a.bar());
Jonas Wilms
  • 106,571
  • 13
  • 98
  • 120