olefrank
olefrank

Reputation: 6810

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
        }
    }

}

Upvotes: 5

Views: 4842

Answers (1)

Dick van den Brink
Dick van den Brink

Reputation: 14537

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.

Upvotes: 6

Related Questions