Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

KeyListener in TypeScript and "this" context

I'm writing a Snake game in TypeScript and have trouble defining a KeyListener (arrow keys for changing Snake direction).

I have a 4 layer architecture and key events are handled in the Gui Class. This holds the Snake object, draws the snake and handles key events.

I tried the normal way, but inside handleEvt the snake object is undefined.

document.addEventListener("keydown", handleEvt)

So I tried the fat arrow method, but now the function doesn't get called at all. I suspect that the key listener changed context and is not working on the window anymore

document.addEventListener("keydown", () => handleEvt)

Can anyone explain what the problem is here? It would be much appreciated!

Below is my Gui Class:

/// <reference path="../model/Snake.ts" />
/// <reference path="../model/Direction.ts" />
/// <reference path="../model/Part.ts" />
/// <reference path="../dao/Dao.ts" />
/// <reference path="../service/Service.ts" />
/// <reference path="../model/IPosObject.ts" />

module gui {

    export class Gui {
        snake:model.Snake;
        canvas:HTMLCanvasElement;
        ctx:CanvasRenderingContext2D;
        loop:any;

        lineWidth:number;
        canvasSize:number;
        loopSpeed:number;
        unitLength:number;

        constructor(snake:model.Snake) {
            // init constants
            this.snake = snake;
            this.lineWidth = 3;
            this.canvasSize = 200;
            this.loopSpeed = 1000/60;
            this.unitLength = 5;

            // init canvas
            this.canvas = document.getElementsByTagName("canvas")[0];
            this.canvas.width = this.canvasSize;
            this.canvas.height = this.canvasSize;
            this.ctx = this.canvas.getContext("2d");

            // Attach key event
//            document.addEventListener("keydown", this.handleEvt);
            document.addEventListener("keydown", () => this.handleEvt);

            // activate game loop
            this.loop = setInterval( () => this.gameLoop(), this.loopSpeed );
        }

        handleEvt(e):void {
            var direction:model.Direction;

            if (e) {
                switch (e.which) {
                    case 37:
                        console.log("left");
                        direction = model.Direction.Left;
                        break;
                    case 38:
                        console.log("up");
                        direction = model.Direction.Up;
                        break;
                    case 39:
                        console.log("right");
                        direction = model.Direction.Right;
                        break;
                    case 40:
                        console.log("down");
                        direction = model.Direction.Down;
                        break;
                }

                this.snake.changeDirection(direction);
            }
        }

        gameLoop():void {
            if (this.snake) {
                this.drawSnake();
            }
        }

        drawSnake() {
          // draw parts
        }
    }

}
like image 500
olefrank Avatar asked Apr 11 '15 08:04

olefrank


People also ask

How can typescript help with the author of a function?

And TypeScript can help greatly! A way I like to think about this is that in regular functions (with the function keyword or the object function short-hand), resolve to “the nearest object”, which is the object that they are bound to. For example: In the example above, hi is bound to author, so this is author.

What is thistype<T> in typescript?

It tells TypeScript that within all functions of methods, this can access to fields from type Data and Methods. Typing this minimalistic version of Vue looks like that: ThisType<T> in lib.es5.d.ts itself is empty.

Why is typescript used for react event handlers?

This also explains some behaviour in React class components with event handlers: TypeScript is pretty good at finding the “nearest object” or knowing the lexical scope, so TypeScript can give you exact information on what to expect from this. There are however some edge cases where we can help a little.

What is an event listener in JavaScript?

Whenever an HTML element is clicked, the most simple case, an event is dispatched. Developers can intercept these events (JavaScript engines are event-driven) with an event listener. Event listeners in the DOM have access to this because the function runs in the context object who fired up the event (an HTML element most of the times).


1 Answers

There are multiple solutions to this problem, you can fix it on your addEventListener side by doing the same as with you did with the setInterval. In the keyDown listener you aren't calling the handleEvt so change it to () => this.handleEvt().

Another solution where you can use it like you did above is this: document.addEventListener("keydown", this.handleEvt);

And declaring the handleEvt like this:

handleEvt = (e) => {
    var direction:model.Direction;
    ...<snip>
}

Or another solution: document.addEventListener("keydown", this.handleEvt.bind(this));

As you can see, there are different solutions and you can choose the one you like. The this is lost because the function isn't called on a instance and that is normal (but sometimes confusing) JavaScript behaviour.

like image 159
Dick van den Brink Avatar answered Oct 03 '22 19:10

Dick van den Brink