Qual é a maneira do AngularJS de criar atalhos de teclado globais?

Eu suponho que eu deveria usar diretiva, mas parece estranho para adicionar uma diretiva a um corpo, mas escute events em um documento.

Qual é a maneira correta de fazer isso?

ATUALIZAÇÃO: Encontrei a UI do AngularJS e vi a realização da diretiva keypress.

Aqui está como eu fiz isso com jQuery – acho que há uma maneira melhor.

 var app = angular.module('angularjs-starter', []); app.directive('shortcut', function() { return { restrict: 'E', replace: true, scope: true, link: function postLink(scope, iElement, iAttrs){ jQuery(document).on('keypress', function(e){ scope.$apply(scope.keyPressed(e)); }); } }; }); app.controller('MainCtrl', function($scope) { $scope.name = 'World'; $scope.keyCode = ""; $scope.keyPressed = function(e) { $scope.keyCode = e.which; }; }); 
   

View keys pressed

{{keyCode}}

Demonstração de Plunker

Eu diria que uma maneira mais adequada (ou “maneira angular”) seria adicioná-lo a uma diretiva. Aqui está um simples para você ir (basta adicionar o atributo keypress-events para ):

 angular.module('myDirectives', []).directive('keypressEvents', [ '$document', '$rootScope', function($document, $rootScope) { return { restrict: 'A', link: function() { $document.bind('keypress', function(e) { console.log('Got keypress:', e.which); $rootScope.$broadcast('keypress', e); $rootScope.$broadcast('keypress:' + e.which, e); }); } }; } ]); 

Na sua diretiva, você pode simplesmente fazer algo assim:

 module.directive('myDirective', [ function() { return { restrict: 'E', link: function(scope, el, attrs) { scope.keyPressed = 'no press :('; // For listening to a keypress event with a specific code scope.$on('keypress:13', function(onEvent, keypressEvent) { scope.keyPressed = 'Enter'; }); // For listening to all keypress events scope.$on('keypress', function(onEvent, keypressEvent) { if (keypress.which === 120) { scope.keyPressed = 'x'; } else { scope.keyPressed = 'Keycode: ' + keypressEvent.which; } }); }, template: '

{{keyPressed}}

' }; } ]);

Use $document.bind :

 function FooCtrl($scope, $document) { ... $document.bind("keypress", function(event) { console.debug(event) }); ... } 

Eu não posso garantir isso ainda, mas eu comecei a dar uma olhada no AngularHotkeys.js:

http://chieffancypants.github.io/angular-hotkeys/

Vai atualizar com mais informações uma vez que eu tenho meus dentes para ele.

Atualização 1: Oh, há um pacote nuget: teclas de atalho angulares

Atualização 2: na verdade, muito fácil de usar, basta configurar sua binding no seu roteiro ou, como eu estou fazendo, no seu controlador:

 hotkeys.add('n', 'Create a new Category', $scope.showCreateView); hotkeys.add('e', 'Edit the selected Category', $scope.showEditView); hotkeys.add('d', 'Delete the selected Category', $scope.remove); 

Aqui está um exemplo de um serviço AngularJS para atalhos de teclado: http://jsfiddle.net/firehist/nzUBg/

Pode então ser usado assim:

 function MyController($scope, $timeout, keyboardManager) { // Bind ctrl+shift+d keyboardManager.bind('ctrl+shift+d', function() { console.log('Callback ctrl+shift+d'); }); } 

Atualização: agora estou usando teclas de atalho angulares .

Como directiva

Isto é essencialmente como é feito no código de documentação Angular, ou seja, pressionando / para iniciar a pesquisa.

 angular .module("app", []) .directive("keyboard", keyboard); function keyboard($document) { return { link: function(scope, element, attrs) { $document.on("keydown", function(event) { // if keycode... event.stopPropagation(); event.preventDefault(); scope.$apply(function() { // update scope... }); } }; } 

Plunk usando uma diretiva de teclado

http://plnkr.co/edit/C61Gnn?p=preview


Como um serviço

Converter essa diretiva em um serviço é muito fácil. A única diferença real é que o escopo não é exposto no serviço. Para acionar um resumo, você pode trazer o $rootScope ou usar um $timeout .

 function Keyboard($document, $timeout, keyCodes) { var _this = this; this.keyHandlers = {}; $document.on("keydown", function(event) { var keyDown = _this.keyHandlers[event.keyCode]; if (keyDown) { event.preventDefault(); $timeout(function() { keyDown.callback(); }); } }); this.on = function(keyName, callback) { var keyCode = keyCodes[keyName]; this.keyHandlers[keyCode] = { callback: callback }; return this; }; } 

Agora você pode registrar retornos de chamada em seu controlador usando o método keyboard.on() .

 function MainController(keyboard) { keyboard .on("ENTER", function() { // do something... }) .on("DELETE", function() { // do something... }) .on("SHIFT", function() { // do something... }) .on("INSERT", function() { // do something... }); } 

Versão alternativa do Plunk usando um serviço

http://plnkr.co/edit/z9edu5?p=preview

A resposta ligeiramente mais curta é apenas olhar para a solução 3 abaixo. Se você gostaria de saber mais opções, você pode ler a coisa toda.

Eu concordo com jmagnusson. Mas acredito que haja uma solução mais limpa. Em vez de ligar as chaves com funções na diretiva, você deve apenas ligá-las em html como definir um arquivo de configuração, e as teclas de atalho devem ser contextuais.

  1. Abaixo está uma versão que usa o mouse trap com uma diretiva personalizada. (Eu não era o autor desse violino)

     var app = angular.module('keyExample', []); app.directive('keybinding', function () { return { restrict: 'E', scope: { invoke: '&' }, link: function (scope, el, attr) { Mousetrap.bind(attr.on, scope.invoke); } }; }); app.controller('RootController', function ($scope) { $scope.gotoInbox = function () { alert('Goto Inbox'); }; }); app.controller('ChildController', function ($scope) { $scope.gotoLabel = function (label) { alert('Goto Label: ' + label); }; }); 

    Você precisará include o mousetrap.js e usá-lo como abaixo:

     
    Click in here to gain focus and then try the following key strokes
    • "gi" to show a "Goto Inbox" alert
    • "gl" to show a "Goto Label" alert

    http://jsfiddle.net/BM2gG/3/

    A solução exige que você inclua o mousetrap.js, que é uma biblioteca que ajuda a definir as teclas de atalho.

  2. Se você quer evitar o problema de desenvolver sua própria diretiva personalizada, você pode conferir este lib:

    https://github.com/drahak/angular-hotkeys

    E isto

    https://github.com/chieffancypants/angular-hotkeys

    O segundo fornece um pouco mais de resources e flexibilidade, ou seja, folha de dicas de chave de access gerada automaticamente para o seu aplicativo.

Atualização : a solução 3 não está mais disponível no Angular ui.

  1. Além das soluções acima, há outra implementação feita pela equipe angularui. Mas a desvantagem é que a solução depende do JQuery lib, que não é a tendência na comunidade angular. (Comunidade Angular tenta apenas usar o jqLite que vem com o angularjs e se livrar de dependencies com overkill). Aqui está o link

    http://angular-ui.github.io/ui-utils/#/keypress

O uso é assim:

No seu html, use o atributo ui-keydown para vincular chave e funções.

  

Na sua diretiva, adicione essas funções no seu escopo.

 app.directive('yourDirective', function () { return { restrict: 'E', templateUrl: 'your-html-template-address.html' link: function(){ scope.cancelModal() = function (){ console.log('cancel modal'); }; scope.tabWatch() = function (){ console.log('tabWatch'); }; scope.initOrSetModel() = function (){ console.log('init or set model'); }; } }; }); 

Depois de brincar com todas as soluções, eu recomendaria o que é implementado pela equipe de UI angular, solução 3 que evitou muitos pequenos problemas estranhos que eu encontrei.

Eu fiz um serviço para atalhos.

Parece que:

 angular.module('myApp.services.shortcuts', []) .factory('Shortcuts', function($rootScope) { var service = {}; service.trigger = function(keycode, items, element) { // write the shortcuts logic here... } return service; }) 

E eu injetei em um controlador:

 angular.module('myApp.controllers.mainCtrl', []) .controller('mainCtrl', function($scope, $element, $document, Shortcuts) { // whatever blah blah $document.on('keydown', function(){ // skip if it focused in input tag if(event.target.tagName !== "INPUT") { Shortcuts.trigger(event.which, $scope.items, $element); } }) }) 

Isso funciona, mas você pode notar que eu injetar $ element e $ document no controlador.

É uma má prática de controle e viola a convenção ‘Não SEMPRE acesse $ elemento no controlador’.

Eu deveria colocá-lo em diretiva, então use ‘ngKeydown’ e $ event para acionar o serviço.

Mas eu acho que o serviço está bom e vou retrabalhar o controlador mais cedo.


Atualizada:

Parece que o ‘ng-keydown’ só funciona em tags de input.

Então eu apenas escrevo uma diretiva e informo $ document:

 angular.module('myApp.controllers.mainCtrl', []) .directive('keyboard', function($scope, $document, Shortcuts) { // whatever blah blah return { link: function(scope, element, attrs) { scope.items = ....;// something not important $document.on('keydown', function(){ // skip if it focused in input tag if(event.target.tagName !== "INPUT") { Shortcuts.trigger(event.which, scope.items, element); } }) } } }) 

É melhor.

Veja este exemplo da revista ng-newsletter.com; Confira seu tutorial sobre como criar um jogo 2048, ele tem algum código legal usando um serviço para events de teclado.

A seguir, vamos escrever toda a sua lógica de atalho no seu controlador e a diretiva cuidará de todo o resto.

Directiva

 .directive('shortcuts', ['$document', '$rootScope', function($document, $rootScope) { $rootScope.shortcuts = []; $document.on('keydown', function(e) { // Skip if it focused in input tag. if (event.target.tagName !== "INPUT") { $rootScope.shortcuts.forEach(function(eventHandler) { // Skip if it focused in input tag. if (event.target.tagName !== 'INPUT' && eventHandler) eventHandler(e.originalEvent, e) }); } }) return { restrict: 'A', scope: { 'shortcuts': '&' }, link: function(scope, element, attrs) { $rootScope.shortcuts.push(scope.shortcuts()); } }; }]) 

Controlador

  $scope.keyUp = function(key) { // H. if (72 == key.keyCode) $scope.toggleHelp(); }; 

Html

 

você pode experimentar esta biblioteca que torna muito fácil gerenciar teclas de atalho, automaticamente vincula e desassocia as teclas enquanto você navega no aplicativo

teclas de atalho angulares

Eu não sei se é um jeito angular real, mas o que eu fiz

 $(document).on('keydown', function(e) { $('.button[data-key=' + String.fromCharCode(e.which) + ']').click(); }); 
ButtonLabel