1

I have the following code inside my widget class that should manage a simple authentication popup.

private showPopupAuth(): void {

    //...just show() and hide() some stuff...

    this.getDiv().find('#cancel').unbind();
    this.getDiv().find('#cancel').click(() => {
        this.clearAuthPopups();                        
    });

    this.getDiv().find('#confirm').unbind();
    this.getDiv().find('#confirm').click(() => {

        let authRequest = new AuthRequest();    
        authRequest.userId = this.userId;
        authRequest.pw = this.getDiv().find('#pw').val();
        this.clearAuthPopups();

        //...do stuff...
    });
}

By pressing the cancel button a private function that closes it is called, and it works perfectly as expected. By pressing the confirm button I get the following error in console

MyWidget.ts:460 Uncaught TypeError: Cannot read property 'userId' of undefined
    at HTMLButtonElement.<anonymous> (MyWidget.ts:460)
    at HTMLButtonElement.dispatch (jquery.js:3)
    at HTMLButtonElement.r.handle (jquery.js:3)

If I try to debug it and check Chrome > Sources > Scope window i see this:

inside cancel callback

  • Local: this correctly points to MyWidget
  • Closure(showPopupAuth): this correctly points to MyWidget

inside confirm callback

  • Local: this is undefined
  • Closure(showPopupAuth): this correctly points to MyWidget

If I run console.log(this) inside showPopupAuth() and inside the two callbacks i get, respectively:

 >   console.log(this)
 <   MyWidget { ..., userId: "the-user-id", ...}

 >   console.log(this)
 <   <button id="cancel">Cancel</button>

 >   console.log(this)
 <   <button id="confirm">Ok</button>

According to the console, this becomes HTMLButtonElement even if I'm using fat arrows in both callbacks.

Why is this happening? What is going on? Looks like scope explorer and console are saying two different things, and both make no sense to me.


I know that I can solve the issue by simply declaring a let _this = this and use it inside the callbacks but I would like to understand the root of the problem.

I already read TypeScript "this" scoping issue when called in jquery callback but it does not answer my question.


Update: as suggested i checked the generated js and everything looks ok

MyWidget.prototype.showPopupAuth = function () {
    var _this = this;
    this.getDiv().find('#cancel').unbind();
    this.getDiv().find('#cancel').click(function () {
        _this.clearAuthPopups();
    });
    this.getDiv().find('#confirm').unbind();
    this.getDiv().find('#confirm').click(function () {
        var authRequest = new AuthServiceBeans_1.AuthRequest();
        authRequest.userId = _this.userId;
        authRequest.pw = _this.getDiv().find('#pw').val();
        _this.clearAuthPopups();
    });
};
marcopiii
  • 793
  • 7
  • 25
  • 2
    Is that really all the code involved? What you're describing can't happen if what you posted is really all there is; if the outer-scope `this` were `undefined` the expression that adds the handler would have failed. – Pointy Mar 27 '19 at 14:43
  • @Pointy The snippet is from a function`showAuthPopup()` where nothing more than some `show()` and `hide()` happens. It is called in a more complex `render()` function as `_this.showAuthPopup()` because there `this` is not actually `MyWidget` (and that's ok) but it can't be the problem since as I explained at the beginning of execution of the snippet `this` acually refers to `MyWidget`, as I expect. – marcopiii Mar 27 '19 at 15:05
  • I will update the snippet anyway to make it more clear – marcopiii Mar 27 '19 at 15:09
  • 2
    Let me put it this way: if `this` is correct in the line that sets up the "click" handler, then it is simply not possible for `this` to be `undefined` inside the `=>` handler function if the code looks like what you posted. – Pointy Mar 27 '19 at 15:21
  • @Pointy this is exactly why I and other 3 colleagues are going crazy. And what about the fact that also in the working _cancel_ callback the console says that `this` is the button while the scope explorer says that it is the widget? – marcopiii Mar 27 '19 at 15:33
  • 1
    Well if you `console.log()` what `this` is *outside* and *inside* those callback functions, it will be the same. I mean, that's simply how the language works. There's of course the possibility that you've found a bug in your JavaScript implementation, but that is highly, highly unlikely. – Pointy Mar 27 '19 at 15:36
  • @Pointy I tried right now. Inside `showAuthPopup()` (but _outside_ the callbacks) I get `MyWidget { ..., userId: "the-user-id", ...}` as expected. Inside the callbacks I get `` and ``. As i said **this makes no sense** because also the _cancel_ callback should break! – marcopiii Mar 27 '19 at 15:51
  • Have you checked the code that Typescript is generating for that? It seems pretty unlikely that that would be wrong, but it's probably slightly more likely than the underlying JavaScript environment being broken. – Pointy Mar 27 '19 at 16:02
  • @Pointy I edited the answer adding the js code – marcopiii Mar 27 '19 at 16:27
  • 1
    Hmm. I can't think of any way that it's possible for that to wind up with `_this` being `undefined`. – Pointy Mar 27 '19 at 18:51
  • @Bergi obviously I meant "declaring a `let _this = this` [outside] and use it inside the callbacks". And it actually works. – marcopiii Mar 28 '19 at 10:05
  • 1
    @Pointy I've been suggested to rename `_this` used in other parts of the code to `self` as the ts compiler already uses it and it may be overridden. And it worked! I did not understand it 100% but I think that those usage of `_this`, even if it were out of scope (the scoping at _typescript-level_ was actually correct), caused confusion on the generated javascript. – marcopiii Apr 03 '19 at 09:19

0 Answers0