34

What would be proper way of creating global keyboard shortcuts (a.k.a. hotkeys) in Angular2 application?

Let's say good starting point would be to get working: "?" for cheat-sheet and "Alt+s" for submitting form.

Should I map "?" somehow to main component and then develop attribute directive that would be applied to those components which should respond on particular hotkeys, but then - how do I prevent input fields from responding to "?".

Andris Krauze
  • 2,092
  • 8
  • 27
  • 39

3 Answers3

49

You can use this syntax in your template

<div (window:keydown)="doSomething($event)">click me<div>

to call this method in your component

doSomething($event) {
  // read keyCode or other properties 
  // from event and execute a command
} 

To listen on the host component itself

@Component({
  selector: 'app-component',
  host: { '(window:keydown)': 'doSomething($event)' },
})
class AppComponent { 
  doSomething($event) {
    ...
  }
}

or by this equivalent syntax

@Component({
  selector: 'app-component',
})
class AppComponent { 
  @HostListener('window:keydown', ['$event'])
  doSomething($event) {
    ...
  }
}
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Seems `?` is not supported for '(window:keydown.?)`. Probably only the codes listed in https://github.com/angular/angular/blob/630d93150a58581a0d474ebf1befb5d09b6813c5/modules/angular2/src/platform/browser/browser_adapter.dart – Günter Zöchbauer Feb 24 '16 at 11:30
  • 1
    On my keyboard "?" is actually "Shift+/", and (window:keydown) catches them separately. – Andris Krauze Feb 24 '16 at 11:51
  • I'm not sure what to make from your last comment. Is it a question or a clarification, ...? – Günter Zöchbauer Feb 24 '16 at 11:54
  • 1
    It was my clarification attempt: by typing "?" windows:keydown event fired 2 keys on my test example: 16 (for shift) and 191 (must be for "?"). Thus seems like "?" is supported. – Andris Krauze Feb 24 '16 at 12:09
  • Maybe a misunderstanding. You can add some keys directly to the event name like `(window:keydown.F1)` but I think that doesn't work for `?` like `(window:keydown.?)`. – Günter Zöchbauer Feb 24 '16 at 12:27
  • Is this typescript compatible? the ```(window: keydown)``` says cannot find name 'keydown' – RobSeg Jun 07 '16 at 09:17
  • Have you tried without space? Where did you actually add `(window: keydown)`? – Günter Zöchbauer Jun 07 '16 at 09:18
  • Tried without space, I added it in ```AppComponent``` above constructor – RobSeg Jun 07 '16 at 12:08
  • I guess I got it. My answer wasn't too clear. I updated it. I hope this solves your issue. `(window:keydown)` can't be used on it's own in TypeScript. – Günter Zöchbauer Jun 07 '16 at 12:28
  • Thanks for the addition, but my use case is not applicable to specific DOM elements. I'd like to have global key triggers independent of binding to DOM – RobSeg Jun 07 '16 at 13:18
  • The last two examples are added to a component but they are listening to events on `window`. There is no Angular application without at least one component, therefore this shouldn't be an issue. – Günter Zöchbauer Jun 07 '16 at 13:22
  • It seems that with the latest rc.4 you need to use `HostListener` instead of `HostBinding`. – Dimanoid Jul 27 '16 at 10:46
  • @Dimanoid thanks for the hint. It always was `HostListener`, I just made a mistake. – Günter Zöchbauer Jul 28 '16 at 04:19
  • 1
    @GünterZöchbauer how would one identify that this is a global event and not a regular input event? E.g. I have a form or a standalone input and if the user is typing there, I still get the global events. – Zlatko Sep 29 '16 at 10:46
  • If it's a bubbling event, then this is normal behavior. You can either call `stopPropagation()` on the event to prevent bubbling or you can get the original element that dispatched the event from the received event itself (check the HTML DOM event docs for details). – Günter Zöchbauer Sep 29 '16 at 10:49
12

You can use a library I created called angular2-hotkeys

It lets you create hotkeys like this:

constructor(private _hotkeysService: HotkeysService) {
  this._hotkeysService.add(new Hotkey('meta+shift+g', (event: KeyboardEvent) => {
    console.log('Typed hotkey');
    return false; // Prevent bubbling
}));  }
Nick Richardson
  • 187
  • 2
  • 9
  • I tried the library but when I build I get semantics errors e.g. ... node_modules/angular2-hotkeys/src/directives/hotke ys.directive.ts(11,110): error TS2304: Cannot find name 'ExtendedKeyboardEvent'. ... and more . – ng-flo Aug 11 '16 at 17:47
  • You can fix it by following the guide in this github issue to fix it: https://github.com/brtnshrdr/angular2-hotkeys/issues/7 – Nick Richardson Aug 12 '16 at 01:36
  • Could you submit an issue with some more information? Thanks! https://github.com/brtnshrdr/angular2-hotkeys/issues – Nick Richardson Feb 13 '17 at 16:25
  • Works well. But can I also put all my hotkeys in an angular service, or does it have to be within a module @NickRichardson? – Heribert Apr 17 '19 at 07:25
0

A simple and elegant solution will look like as following:-

Create a global service, which will store the bindings of shortcuts with clickable elements. Note:- Use NgOnDestroy, for removing bindings on component is destroyed.

Now create a directive, which will take keycodes as input like this.

<button [angularHotKey]="[17,78]">New Document</button>
<!-- CTRL = 17 & n = 78 -->

Now in your root most component, listen keypresses and use them as indices for locating Elements in Global Service for HotKeys. Now on getting the reference do something like this.

I assume you have formatted recorded keycodes in this format = 1-23-32-... , dont forget to sort them in ascending/descending format, both while adding it to keypair array in service and while checking

if(this.keypair[combo].length)this.keypair[combo].click();

this.keypair[combo] contain reference of element on which angularHotKey directive was added.

Further Notes: 1.) In angularHotKey directive while adding new keypair and element/clickable reference please check if there don't exist pairing with same combo, if yes throw a exception, it will be helpful while debugging, and prevent you from doing silly mistakes, and in Angular 2 component class,in ngOnDestroy method define a logic of removing all shortcuts paired with it's child elements.

Visit http://keycode.info/ for getting keycodes for all types of keys present on your keyboard.

JavaScript multiple keys pressed at once A answer with every little detail about how to deal with combo key presses

Ravinder Payal
  • 2,884
  • 31
  • 40