23

Trying to monitor keyboard events with Angular 2 using TypeScript and What is Angular2 way of creating global keyboard shortcuts (a.k.a. hotkeys)? was helpful but tslint (codelyzer) objects with the message

In the "@Component" class decorator you are using the "host" property, this is considered bad practice. Use "@HostBindings", "@HostListeners" property decorator instead.

How do I use the recommended decorators? I'm not sure how the examples in Angular 2: Host binding and Host listening apply to my use case as I am not binding to any DOM elements.

Here is my demo

@Component({
  selector: 'my-app',
 template: `
    <div>
      <h2>Keyboard Event demo</h2>
      Start typing to see KeyboardEvent values
    </div>
    <hr />
    KeyboardEvent
    <ul>
      <li>altKey: {{altKey}}</li>
      <li>charCode: {{charCode}}</li>
      <li>code: {{code}}</li>
      <li>ctrlKey: {{ctrlKey}}</li>
      <li>keyCode: {{keyCode}}</li>
      <li>keyIdentifier: {{keyIdentifier}}</li>
      <li>metaKey: {{metaKey}}</li>
      <li>shiftKey: {{shiftKey}}</li>
      <li>timeStamp: {{timeStamp}}</li>
      <li>type: {{type}}</li>
      <li>which: {{which}}</li>
    </ul>
      `,
  host: { '(window:keydown)': 'keyboardInput($event)' }
  /*
  In the "@Component" class decorator you are using the "host" property, this is considered bad practice. 
  Use "@HostBindings", "@HostListeners" property decorator instead.
  */

})
export class App {

  /* a few examples */
  keyboardEvent: any;
  altKey: boolean;
  charCode: number;
  code: string;
  ctrlKey: boolean;
  keyCode: number;
  keyIdentifier: string;
  metaKey: boolean;
  shiftKey: boolean;
  timeStamp: number;
  type: string;
  which: number;

  keyboardInput(event: any) {
    event.preventDefault();
    event.stopPropagation();

    this.keyboardEvent = event;
    this.altKey = event.altKey;
    this.charCode = event.charCode;
    this.code = event.code;
    this.ctrlKey = event.ctrlKey;
    this.keyCode = event.keyCode;
    this.keyIdentifier = event.keyIdentifier;
    this.metaKey = event.metaKey;
    this.shiftKey = event.shiftKey;
    this.timeStamp = event.timeStamp;
    this.type = event.type;
    this.which = event.which;
  }

}

https://plnkr.co/edit/Aubybjbkp7p8FPxqM0zx

Frederik Struck-Schøning
  • 12,981
  • 8
  • 59
  • 68
james sloan
  • 388
  • 1
  • 3
  • 9

2 Answers2

35
import {HostListener} from '@angular/core';

@HostListener('window:keydown', ['$event'])
handleKeyDown(event: KeyboardEvent) {
  // event.key === 'ArrowUp'
}
  • @HostBindings('attr.foo') foo = 'bar' is to bind values from your component instance to the host element like class, attributes, properties or styles.
Slava Fomin II
  • 26,865
  • 29
  • 124
  • 202
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 3
    I have seen it mentioned in comments that using `host: {}` is discouraged and `@HostBinding()`, `@HostListener()` are preferred, but I haven't seen this mentioned from the Angular team. – Günter Zöchbauer Apr 18 '16 at 14:29
  • How do I do the same for esc key? – user1532043 Nov 05 '16 at 16:04
  • There is no difference. Add `if (event.keyCode == 27) { ... } ` to check if the event is the esc key. – Günter Zöchbauer Nov 05 '16 at 16:40
  • 1
    @GünterZöchbauer They did, check it out [here](https://angular.io/docs/ts/latest/guide/style-guide.html#!#06-03) – realappie Apr 20 '17 at 12:08
  • I also recommend you to stay away from `host` as `HostListener` has worked better for key event combinations for me. – realappie Apr 20 '17 at 12:37
  • 1
    While `@HostListener` works great, it also makes (my) Angular change detection run **for the parent component** on each key press. Using `Observable.fromEvent(window, 'keydown').subscribe(...)` does not. I guess one could create a decorator to run the decorated method outside of Angular's zone, but I've not tried. – Arjan Apr 26 '18 at 10:51
1

Template (.html)

<div (keydown)="keyPressed($event)">...</div>

Class (.ts)

  keyPressed(keyEvent: KeyboardEvent): void {
    if (keyEvent.key === 'ArrowLeft') {
      console.log('LEFT');
    }
    else if (keyEvent.key === 'ArrowRight') {
      console.log('RIGHT');
    }
  }

The element where you put the event listener or any of its children must be focused to work. For capturing globally use <div (window:keydown)...>.

I think this is a more Angular way™ alternative. From the docs.

Eneko
  • 1,709
  • 1
  • 16
  • 25