ti-enxame.com

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 eventos em um documento.

Qual é a maneira correta de fazer isso?

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

74
ValeriiVasin

Aqui está como eu fiz isso com jQuery - eu 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;
  };
});
<body ng-controller="MainCtrl">
  <shortcut></shortcut>
  <h1>View keys pressed</h1>
  {{keyCode}}
</body>

demo Plunker

9
Jason

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 <body>):

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: '<h1>{{keyPressed}}</h1>'
    };
  }
]);
69
jmagnusson

Use $document.bind:

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

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 ligação em sua rota ou como eu estou fazendo, em 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);
20
A. Murray

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');
    });
}

Update: Agora estou usando teclas de atalho angulares .

10
Tom Söderlund

Como uma diretiva

Isso é essencialmente como é feito no código de documentação Angular, isto é, 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 $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

7
Blake Bowen

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 o jmagnusson. Mas acredito que haja uma solução mais limpa. Ao invés de ligar as teclas 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 deste 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á incluir o mousetrap.js e usá-lo como abaixo:

    <div ng-app="keyExample">
        <div ng-controller="RootController">
            <keybinding on="g i" invoke="gotoInbox()" />
            <div ng-controller="ChildController">
                <keybinding on="g l" invoke="gotoLabel('Sent')" />
            </div>
        </div>
        <div>Click in here to gain focus and then try the following key strokes</div>
        <ul>
            <li>"g i" to show a "Goto Inbox" alert</li>
            <li>"g l" to show a "Goto Label" alert</li>
        </ul>
    </div>
    

    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ê quiser evitar o problema de desenvolver sua própria diretiva personalizada, você pode verificar este lib:

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

    E isto

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

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

Atualização : a solução 3 não está mais disponível em 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 da biblioteca do JQuery, 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 dependências exageradas). Aqui está o link

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

O uso é assim:

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

<div class="modal-inner" ui-keydown="{
                        esc: 'cancelModal()',
                        tab: 'tabWatch($event)',
                        enter: 'initOrSetModel()'
                    }">

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 Angular equipe de interface do usuário, solução 3 que evitou muitos pequenos problemas estranhos que eu encontrei.

4
Tim Hong

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);
     }
   })
})

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 TENHA acesso a $ elemento no controlador'.

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

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


atualizado:

Parece que o 'ng-keydown' só funciona em tags de entrada.

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.

1
zisasign

eu não sei se é um verdadeiro angular caminho, mas o que eu fiz

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

<div class="button" data-key="1" ng-click="clickHandler($event)">
    ButtonLabel         
</div>
0
wutzebaer

Verifique isto exemplo dos sujeitos behid ng-newsletter.com; check seu tutorial ao criar um jogo 2048, ele possui algum código de Nice usando um serviço para eventos de teclado.

0
numediaweb

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

diretiva

.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

<div shortcuts="keyUp">
    <!-- Stuff -->
</div>
0
Reimund

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

0
Ahmed Ahmed