Angular 2+ elegant way to intercept Command+S
Asked Answered
S

3

14

Trying to implement a shortcut key combination for Command+S to save a form.

I've read this - https://angular.io/guide/user-input, but it does not say anything about meta or command.

Tried surrounding the form with:

<div
  (keyup.command.s)="save()"
  (keyup.command.u)="save()"
  (keyup.control.u)="save()"
  (keyup.control.s)="save()"
  (keyup.meta.u)="save()"
>

Of those, only control.u and control.s worked.

With all the power and cross-browser capabilities of Angular 2+, I was hoping that this is somehow handled in an elegant way, using (keyup...).

And for sure many Angular Devs use Macs :).

I've also read How does one capture a Mac's command key via JavaScript? and http://unixpapa.com/js/key.html but still hoping for Angular elegant solution instead of fighting with browser-specific stuff...

Ster answered 8/8, 2017 at 12:48 Comment(0)
B
8

UPDATE

To stop the save dialog of the browser from opening, we must use the keydown event instead of keyup and call the function $event.preventDefault();. Updated code below:

  onKeyDown($event): void {
    // Detect platform
    if(navigator.platform.match('Mac')){
        this.handleMacKeyEvents($event);
    }
    else {
        this.handleWindowsKeyEvents($event); 
    }
  }

  handleMacKeyEvents($event) {
    // MetaKey documentation
    // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/metaKey
    let charCode = String.fromCharCode($event.which).toLowerCase();
    if ($event.metaKey && charCode === 's') {
        // Action on Cmd + S
        $event.preventDefault();
    } 
  }

  handleWindowsKeyEvents($event) {
    let charCode = String.fromCharCode($event.which).toLowerCase();
    if ($event.ctrlKey && charCode === 's') {
        // Action on Ctrl + S
        $event.preventDefault();
    } 
  }

Then bind this method to the (keydown) event in your div:

<div (keydown)="onKeyDown($event)" tabindex="0">
</div>

Updated PLUNKER DEMO


ORIGINAL ANSWER

Here is an idea, how about detecting the event in you class:

  onKeyUp($event): void {
    // Detect platform
    if(navigator.platform.match('Mac')){
        this.handleMacKeyEvents($event);
    }
    else {
        this.handleWindowsKeyEvents($event); 
    }
  }

  handleMacKeyEvents($event) {
    // MetaKey documentation
    // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/metaKey
    let charCode = String.fromCharCode($event.which).toLowerCase();
    if ($event.metaKey && charCode === 's') {
        // Action on Cmd + S
        $event.preventDefault();
    } 
  }

  handleWindowsKeyEvents($event) {
    let charCode = String.fromCharCode($event.which).toLowerCase();
    if ($event.ctrlKey && charCode === 's') {
        // Action on Ctrl + S
        $event.preventDefault();
    } 
  }

Then bind this method to the (keyup) event in your div:

<div (keyup)="onKeyUp($event)" tabindex="0">
</div>

Here is a plunker link: PLUNKER DEMO

Brinkman answered 8/8, 2017 at 12:59 Comment(14)
There is an Boolean for "meta" keys, which is the Command key on mac, it's event.metaKey.Clangor
@Ster in mac you have metaKey. See the updated answer.Brinkman
Tried $event.metaKey. Behaves weirdly. I have not yet managed to capture any useful key combination with metaKey... As if it is not a modifier key (which I've read somewhere as well...)Ster
@AdamLeBlanc: have you managed to capture actual meta+letter key combinations using metaKey?Ster
here is a useful article on key combinations: bennadel.com/blog/…Brinkman
@Faisal: for me, your updated example (with $event.metaKey) does not work in neither Chrome nor Firefox nor Safari. For Cmd+S it brings the "Save webpage" dialog. Did you try to run it?Ster
div by default is not focusable. I test with a input element. I have created a plunk for this, here is the link: plnkr.co/edit/xtaTjKzPwIDXpc77ZcUj?p=preview [I am trying to figure out how to stop the save dialog from opening]Brinkman
@Faisal: so far the only thing that was able to capture command+s (key DOWN) - bennadel.github.io/JavaScript-Demos/demos/… - source github.com/bennadel/JavaScript-Demos/blob/master/demos/…Ster
@Faisal: for preventing save dialog, maybe: $event.preventDefault();Ster
@Faisal - interestingly, for me your Plunkr does not seem to detect Cmd+S... Are you on a Mac?Ster
I am on windowsBrinkman
@Ster I have updated the plunker to detect the platformBrinkman
@Faisal I accepted this answer, though the remaining question is how to get rid of the browser's page-save dialog hijacking the shortcut key, which we hope to solve soon ;-).Ster
For global shortcuts, one can use the window: prefix, for example: <div (window:keydown.meta.s)="keyDownSave($event)" (window:keydown.control.s)="keyDownSave($event)" >Ster
L
17

Global listener, non-deprecated answer:

@HostListener('window:keydown', ['$event'])
onKeyDown(event: KeyboardEvent) {
    if ((event.metaKey || event.ctrlKey) && event.key === 's') {
        this.save();
        event.preventDefault();
    }
}
Leigha answered 7/9, 2020 at 1:28 Comment(0)
B
8

UPDATE

To stop the save dialog of the browser from opening, we must use the keydown event instead of keyup and call the function $event.preventDefault();. Updated code below:

  onKeyDown($event): void {
    // Detect platform
    if(navigator.platform.match('Mac')){
        this.handleMacKeyEvents($event);
    }
    else {
        this.handleWindowsKeyEvents($event); 
    }
  }

  handleMacKeyEvents($event) {
    // MetaKey documentation
    // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/metaKey
    let charCode = String.fromCharCode($event.which).toLowerCase();
    if ($event.metaKey && charCode === 's') {
        // Action on Cmd + S
        $event.preventDefault();
    } 
  }

  handleWindowsKeyEvents($event) {
    let charCode = String.fromCharCode($event.which).toLowerCase();
    if ($event.ctrlKey && charCode === 's') {
        // Action on Ctrl + S
        $event.preventDefault();
    } 
  }

Then bind this method to the (keydown) event in your div:

<div (keydown)="onKeyDown($event)" tabindex="0">
</div>

Updated PLUNKER DEMO


ORIGINAL ANSWER

Here is an idea, how about detecting the event in you class:

  onKeyUp($event): void {
    // Detect platform
    if(navigator.platform.match('Mac')){
        this.handleMacKeyEvents($event);
    }
    else {
        this.handleWindowsKeyEvents($event); 
    }
  }

  handleMacKeyEvents($event) {
    // MetaKey documentation
    // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/metaKey
    let charCode = String.fromCharCode($event.which).toLowerCase();
    if ($event.metaKey && charCode === 's') {
        // Action on Cmd + S
        $event.preventDefault();
    } 
  }

  handleWindowsKeyEvents($event) {
    let charCode = String.fromCharCode($event.which).toLowerCase();
    if ($event.ctrlKey && charCode === 's') {
        // Action on Ctrl + S
        $event.preventDefault();
    } 
  }

Then bind this method to the (keyup) event in your div:

<div (keyup)="onKeyUp($event)" tabindex="0">
</div>

Here is a plunker link: PLUNKER DEMO

Brinkman answered 8/8, 2017 at 12:59 Comment(14)
There is an Boolean for "meta" keys, which is the Command key on mac, it's event.metaKey.Clangor
@Ster in mac you have metaKey. See the updated answer.Brinkman
Tried $event.metaKey. Behaves weirdly. I have not yet managed to capture any useful key combination with metaKey... As if it is not a modifier key (which I've read somewhere as well...)Ster
@AdamLeBlanc: have you managed to capture actual meta+letter key combinations using metaKey?Ster
here is a useful article on key combinations: bennadel.com/blog/…Brinkman
@Faisal: for me, your updated example (with $event.metaKey) does not work in neither Chrome nor Firefox nor Safari. For Cmd+S it brings the "Save webpage" dialog. Did you try to run it?Ster
div by default is not focusable. I test with a input element. I have created a plunk for this, here is the link: plnkr.co/edit/xtaTjKzPwIDXpc77ZcUj?p=preview [I am trying to figure out how to stop the save dialog from opening]Brinkman
@Faisal: so far the only thing that was able to capture command+s (key DOWN) - bennadel.github.io/JavaScript-Demos/demos/… - source github.com/bennadel/JavaScript-Demos/blob/master/demos/…Ster
@Faisal: for preventing save dialog, maybe: $event.preventDefault();Ster
@Faisal - interestingly, for me your Plunkr does not seem to detect Cmd+S... Are you on a Mac?Ster
I am on windowsBrinkman
@Ster I have updated the plunker to detect the platformBrinkman
@Faisal I accepted this answer, though the remaining question is how to get rid of the browser's page-save dialog hijacking the shortcut key, which we hope to solve soon ;-).Ster
For global shortcuts, one can use the window: prefix, for example: <div (window:keydown.meta.s)="keyDownSave($event)" (window:keydown.control.s)="keyDownSave($event)" >Ster
S
1
  1. The methods preventDefault() and stopPropagation() and so on did not work for me (using current Chrome), so i had to go multiple keys: ctrl+shift+s
  2. When defining the event handler in my component, it only worked for this subsite and only if some form inputs were in focus, so i had to add the listener at another place.
  3. I compressed the multiple functions, since i do not care about which environment the hotkey gets called, i want to save my object.
ngOnInit() {   
  document.body.addEventListener("keydown", event => {
    let charCode = String.fromCharCode(event.which).toLowerCase();
    switch (((navigator.platform.match('Mac') && event.metaKey) || event.ctrlKey) && event.shiftKey && charCode === 's') {
      case true: {
        this.saveArticle(); //whatever your usecase is
        break;
      }
    }
  });
}
Smitt answered 3/11, 2020 at 6:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.