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
}
}
}
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.
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.
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.
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).
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With