vt5491
vt5491

Reputation: 2294

How to access 'this' in angular 2 event handler?

In my Component's keydown event handler, I need to modify a propery of the component via this:

  canvasKeyHandler (event) {
    console.log('vrscene.canvasKeyHandler: event.keyCode=' + event.keyCode);
    console.log('vrscene.canvasKeyHandler: self.dolly' + this.dolly);

    CameraKeypressEvents.keyHandler(event, this.dolly) // <-- do something with this.dolly

But this.dolly is set to undefined:

vrscene.canvasKeyHandler: self.dollyundefined

vrscene.html:

<div class="container" id ="canvas-container-2">
   <canvas id="vrruntime-view"
           class="camera-keypress-events"
           tabindex="1"
           (keydown)="canvasKeyHandler($event)"
           style="border: 1px solid black;"
           >
   </canvas>
           <!--camera-keypress-events [cubeScene]="cubeScene" -->

</div>

vrscene.ts:

import {Component} from 'angular2/core';
import {Injectable} from 'angular2/core';
import WebGLRenderer = THREE.WebGLRenderer;
import {VRRenderer} from '../vrrenderer/vrrenderer'
import {CameraKeypressEvents} from '../camera-keypress-events/camera-keypress-events'

import Object3D = THREE.Object3D;
import Scene = THREE.Scene;
import PerspectiveCamera = THREE.PerspectiveCamera;
import Mesh = THREE.Mesh;
import VRControls = THREE.VRControls;
import VREffect = THREE.VREffect;

@Component ({
  selector: 'vrscene',
  templateUrl: 'app//vrscene/vrscene.html',
})

@Injectable()
export class VRScene {

  private _scene: Scene;
  camera: PerspectiveCamera;
  dolly: Object3D;
  vrControls: VRControls;
  vrEffect: VREffect;
  webVrManager;
  sphere: Mesh;
  cube: Mesh;
  BaseRotation = new THREE.Quaternion();

  constructor() {}

  //initScene(width: number, height: number, renderer: VRRenderer) {
  init(width: number, height: number, vrRenderer: VRRenderer) {
    console.log('VRScene.init: entered')
    this.scene = new THREE.Scene;

    this.camera = new THREE.PerspectiveCamera(75, width / height);
    this.camera.position.set(0, 1.5, 100);
    this.dolly = new THREE.Object3D();
    this.dolly.position.z = 50;
    this.scene.add(this.dolly);
    //
    this.dolly.add(this.camera);

    this.vrControls = new THREE.VRControls(this.camera);

    this.vrEffect = new THREE.VREffect(vrRenderer.renderer);
    this.vrEffect.setSize(width, height);
    this.webVrManager = new (<any>window).WebVRManager(vrRenderer.renderer, this.vrEffect);
    console.log('VRScene.init: this.webVrManager=' + this.webVrManager);
    this.camera.quaternion.copy(this.BaseRotation);

    var geometry = new THREE.BoxGeometry(25, 25, 25);
    var meshParms = new Object();

    meshParms['color'] = 0xff8000;

    var material = new THREE.MeshBasicMaterial(meshParms);
    //material = new THREE.MeshBasicMaterial( {color: 0xffff00, side: THREE.DoubleSide} );
    this.cube = new THREE.Mesh(geometry, material);
    this.scene.add(this.cube);

    // draw!
    vrRenderer.canvas.focus();
    //CubeOnPlaneScene.prototype.mainLoop.bind(this)
    // bind the 'this' of the canvasKeyHandler to the definition-time 'this'
    //VRScene.prototype.canvasKeyHandler.bind(this)
  }

  canvasKeyHandler (event, dolly) {
    console.log('vrscene.canvasKeyHandler: event.keyCode=' + event.keyCode);
    //console.log('vrscene.canvasKeyHandler: this.dolly' + this.dolly);
    //console.log('vrscene.

canvasKeyHandler: self.dolly' + this.dolly);
    console.log('vrscene.canvasKeyHandler: dolly' + dolly);

    //CameraKeypressEvents.keyHandler(event, this.dolly)
    //CameraKeypressEvents.keyHandler(event, VRScene.prototype.canvasKeyHandler)
    CameraKeypressEvents.keyHandler(event, dolly)
  }

  doIt() : string {
    return 'hello from VRScene'
  }

  // getters  and setters
  get scene():Scene {
     return this._scene;
  }

  set scene(scene: Scene) {
    if (scene === undefined) throw 'Please supply a scene';
    this._scene = scene;
  }

}

Key handler:

import {Directive} from 'angular2/core';
import {CubeScene} from '../cube-scene/cube-scene';
import Object3D = THREE.Object3D;
import Vector3 = THREE.Vector3;
import {Base} from '../base/base';
import Quaternion = THREE.Quaternion;


@Directive({
  selector: '[camera-keypress-events]',
  providers: [],
  //host: {},
  host: {
    '(keypress)' : 'onKeypress($event)'
  },

})

export class CameraKeypressEvents {

  constructor() {}

  static CAMERA_MOVE_DELTA = 1.2;
  static CAMERA_ROT_DELTA = 5;

  static keyHandler (event, dolly: Object3D) {
    console.log('CameraKeypressEvents.keyHandler: event.keyCode=' + event.keyCode)
    console.log('CameraKeypressEvents.keyHandler: dolly=' + dolly)   
        switch( event.keyCode) {
      case 'S'.charCodeAt(0):
        console.log('you pressed s');
        //dolly.position.z += CAMERA_MOVE_DELTA;
        dolly.translateZ(this.CAMERA_MOVE_DELTA);
        console.log('dolly.postion.x=' + dolly.position.x);
      break;

      case 'W'.charCodeAt(0):
        //console.log('you pressed s');
        //this.dolly.position.z -= this.CAMERA_MOVE_DELTA;
        dolly.translateZ(-this.CAMERA_MOVE_DELTA);
        //console.log('this.do-ly.postion.x=' + this.dolly.position.x);
      break;

      case 'A'.charCodeAt(0):
        //this.dolly.position.x -= this.CAMERA_MOVE_DELTA;
        dolly.translateX(-this.CAMERA_MOVE_DELTA);
      break;

      case 'D'.charCodeAt(0):
        //console.log('you pressed s');
        //this.dolly.position.x += this.CAMERA_MOVE_DELTA;
        dolly.translateX(this.CAMERA_MOVE_DELTA);
        //console.log('this.dolly.postion.x=' + this.dolly.position.x);
      break;

      case 'N'.charCodeAt(0):
        //this.dolly.position.y -= this.CAMERA_MOVE_DELTA;
        dolly.translateY(-this.CAMERA_MOVE_DELTA);
      break;

      case 'P'.charCodeAt(0):
        //console.log('you pressed s');
        //this.dolly.position.y += this.CAMERA_MOVE_DELTA;
        //console.log('this.dolly.postion.x=' + this.dolly.position.x);
        dolly.translateY(this.CAMERA_MOVE_DELTA);
      break;

      case 'Q'.charCodeAt(0):
        var tmpQuat = (new THREE.Quaternion()).setFromAxisAngle( new THREE.Vector3(0,1,0), Base.ONE_DEG * this.CAMERA_ROT_DELTA);
        dolly.quaternion.multiply(tmpQuat);
      break;

      case 'E'.charCodeAt(0):
        var tmpQuat = (new THREE.Quaternion()).setFromAxisAngle( new THREE.Vector3(0,1,0), Base.ONE_DEG * -this.CAMERA_ROT_DELTA);
        dolly.quaternion.multiply(tmpQuat);
      break;
    };
  }

   onKeypress (event, cubeScene) {
    console.log('CameraKeypressEvents.onKeypress: event.keyCode=' + event.keyCode)
        //event.preventDefault();
    console.log('vtClass.canvasKeyhandler: cubeScene=' + cubeScene);
    console.log(event, event.keyCode, event.keyIdentifier);

    /*
    */
  }
}

How do I either:

A) pass the instance on the keyhandler call?

I tried:

(keydown)="canvasKeyHandler($event,{{dolly}})"

B) Bind the canvasKeyHandler method to the compile time this?

I tried:

// bind the callback 'this' of the canvasKeyHandler to the definition-time 'this'
VRScene.prototype.canvasKeyHandler.bind(this)

and

  canvasKeyHandler (event) {
    ...
}.bind(this)

My apologies if this is an obvious question, but I'm just learning the Angular 2 framework and getting a little overwhelmed at this point. Do I need to do something with @Input?

Many Thanks

Upvotes: 7

Views: 4741

Answers (3)

vt5491
vt5491

Reputation: 2294

This was a real object lesson in the subtleties of DI for me.

Basically, this problem can be attributed to user error. There weren't any smoking gun issues with angular2 or typescript not working properly. My main problem was using 'init' methods to initialize my component instead of doing it in the constructor. I was doing this because I wanted late binding of properties that I felt may not be known until run time. This is fine, but it appears the injector was not driving my init code (or maybe it was and the properties just weren't defined at the time). There was also the factor of having a component that was sub-classing a parent using inheritance, instead of dependency injection. It looks like the injector might have been only driving the parent class's constructor and not the child's, although I can't definitively say this was the case as I had several issues going on.

I'm in the process of refactoring my code to use DI only, and to put all initialization in the constructors, and overall just being a little tighter about controlling the DI process.

The main takeaway for anyone having a similar problem is to realize that there are three potential contexts in which you component may exist or be created: compile-time, injection-time, and run-time. There may even be load-time, but I'm assuming this is the same as injection-time. "Normally", they're all the same and you don't have to distinguish between them, but just keep in mind if you're doing late dynamic initialization, and/or using inheritance instead of DI, that the injected component may not be in quite the state you expect it to be.

Upvotes: 0

Thierry Templier
Thierry Templier

Reputation: 202176

The this corresponds to the instance of your component within the event handler (canvasKeyHandler). I think that using the following to configure the handler for the keydown event is enough:

(keydown)="canvasKeyHandler($event)"

I saw that you initialize the dolly property within the init method. But I can't see where this method is called. Perhaps it's the reason of your problem...

Upvotes: 1

G&#252;nter Z&#246;chbauer
G&#252;nter Z&#246;chbauer

Reputation: 657278

(keydown)="canvasKeyHandler($event,{{dolly}})"

should be

(keydown)="canvasKeyHandler($event,dolly)"

I don't know if this actually fixes your problem (don't really understand what the issue is)

Upvotes: 2

Related Questions