【问题标题】:What is AngularJS way to create global keyboard shortcuts?AngularJS 创建全局键盘快捷键的方法是什么?
【发布时间】:2013-02-09 06:28:47
【问题描述】:

我想我应该使用指令,但是在正文中添加指令似乎很奇怪,而是在文档上监听事件。

这样做的正确方法是什么?

更新:找到 AngularJS UI 并看到their keypress 指令的实现。

【问题讨论】:

  • 我假设您的意思是键盘快捷键...我也对此感到好奇,我得出的结论是 Angular 不是完成这项任务的最佳工具。我写了一个执行此操作的指令,但存在问题-首先是您所暗示的语义指令,而且我认为将 jquery 包装在指令中并不是一种好习惯,并且在存在时会导致一些令人困惑的情况是多个模板,只有其中一些需要文档快捷方式。
  • 快捷方式需要与我的控制器连接。而且我没有看到外部 jquery 模块的任何好处。我还看到了两种可能的方式:1)jQuery外部快捷方式模块+与控制器的pubsub通信。 2) Angular 指令,这很奇怪,但我想用快捷方式提供链接功能是可以的。
  • 我不认为你可以将 angularjs ui 指令添加到文档中,它们的范围是一个元素。
  • 不需要额外的库...使用$document.bind('keypress')$document
  • 链接现在是404。如果有更新的位置,请更新一下。

标签: javascript angularjs


【解决方案1】:

我想说一种更合适的方式(或“Angular 方式”)是将其添加到指令中。这是一个简单的方法(只需将keypress-events 属性添加到<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);
        });
      }
    };
  }
]);

在您的指令中,您可以简单地执行以下操作:

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

【讨论】:

  • 干得好,真干净。
  • 它绑定到 $document,而不是元素,这对我来说可以在 div 上获取关键事件。 +1 用于展示如何注入 $document。
  • 根据上面的代码,该指令将事件绑定到 window.document ($document) 元素,同时它可以附加到任何 DOM 标签,而不仅仅是 ,因为没有验证。在这种情况下,附加指令的元素可以被销毁,但绑定的事件监听器将保留。我建议要么进行一些验证(将元素限制为 ),要么实现使用 $scope.on('destroy') 取消绑定事件侦听器的方法。
  • 为什么不捕获Escape键?
  • @SaeedNamati 查看 Yehuda Katz 对 Resig 的回复 ejohn.org/blog/keypress-in-safari-31
【解决方案2】:

使用$document.bind:

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

【讨论】:

  • 这个好像有两种方法,一种是创建一个指令并通过函数将$event传递给控制器​​,另一种是直接在控制器中绑定事件。控制器方法似乎更少的代码和相同的结果。是否有理由选择一种方法而不是另一种方法?
  • 这种方法每次按键都会给我多个事件触发器,我使用 mousetrap 而不是 $document.bind,它似乎已经足够了。
  • 这种方法禁用了其他变量的自动更新,{{ abc }}
  • 您需要应用更改var that = this; $document.bind("keydown", function(event) { $scope.$apply(function(){ that.handleKeyDown(event); });
【解决方案3】:

我还不能保证,但我已经开始研究 AngularHotkeys.js:

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

一旦我投入使用,将更新更多信息。

更新 1:哦,有一个 nuget 包:angular-hotkeys

更新 2:实际上非常易于使用,只需在您的路由中或我正在做的那样在您的控制器中设置您的绑定:

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

【讨论】:

    【解决方案4】:

    这是我使用 jQuery 完成此操作的方法 - 我认为有更好的方法。

    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>
    

    Plunker demo

    【讨论】:

    • 感谢您的回复。我看到你认为我们应该按照指令来做。
    • 链接:函数 postLink(scope, iElement, iAttrs){ window.addEventListener('load', function(e){ scope.$apply(scope.keyPressed(e)); }, false) ; }
    • 奇怪的是有一个指令,但没有服务。指令 - 可重用的 ui 组件(在大多数情况下)。
    • 每次我在 Angular 中听到 JQuery 时都会起鸡皮疙瘩
    【解决方案5】:

    这里是一个用于键盘快捷键的 AngularJS 服务示例:http://jsfiddle.net/firehist/nzUBg/

    然后可以这样使用:

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

    更新:我现在改用angular-hotkeys

    【讨论】:

    • 优秀的小提琴,但我们可以绑定指令中的所有快捷方式,以便我可以在我的应用程序中调用吗?
    【解决方案6】:

    作为指令

    这基本上是在 Angular 文档代码中完成的,即按 / 开始搜索。

    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 使用键盘指令

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


    作为服务

    将该指令转换为服务非常容易。唯一真正的区别是范围没有在服务上公开。要触发摘要,您可以引入$rootScope 或使用$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;
      };
    }
    

    您现在可以使用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... });       
    }
    

    使用服务的 Plunk 替代版本

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

    【讨论】:

      【解决方案7】:

      略短的答案就是看看下面的解决方案 3。如果您想了解更多选项,可以阅读全文。

      我同意 jmagnusson 的观点。但我相信有更清洁的解决方案。与其将键与指令中的函数绑定,不如将它们绑定在 html 中,就像定义配置文件一样,并且热键应该是上下文的。

      1. 以下是使用带有自定义指令的鼠标陷阱的版本。 (一世 不是这个小提琴的作者。)

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

        你需要包含mousetrap.js,你可以像下面这样使用它:

        <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/

        该解决方案要求您包含作为库的 mousetrap.js 帮助您定义热键。

      2. 如果您想避免开发自己的自定义的麻烦 指令,你可以查看这个库:

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

        还有这个

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

        第二个提供了更多的功能和灵活性,即 为您的应用自动生成热键备忘单。

      更新:Angular ui 不再提供解决方案 3。

      1. 除了上面的解决方案,还有另外一个实现 通过 angularui 团队。但缺点是解决方案取决于 JQuery lib 这不是 Angular 社区的趋势。 (角度 社区尝试只使用 angularjs 附带的 jqLit​​e 和 摆脱过度依赖的依赖。)这是链接

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

      用法是这样的:

      在您的 html 中,使用 ui-keydown 属性来绑定键和功能。

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

      在您的指令中,将这些函数添加到您的作用域中。

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

      在尝试了所有解决方案之后,我会推荐 Angular UI 团队实现的解决方案 3,它避免了我遇到的许多小奇怪问题。

      【讨论】:

      • 感谢分享。我投了赞成票。实际上, Chieffancypants 的 angular 热键看起来很棒,但不知道如何仅针对特定模型对其进行自定义。 drahak 的角度热键适用于一对一的关系!
      • 我猜这第三个解决方案(ui-utils)不再维护或链接无效。 github repo 被标记为已弃用
      • 感谢 Bernardo 的评论,我将删除第三个解决方案。
      【解决方案8】:

      我为快捷方式提供了服务。

      看起来像:

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

      然后我将它注入到控制器中:

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

      它可以工作,但你可能会注意到我将 $element 和 $document 注入到控制器中。

      这是一种不好的控制器做法,违反了“永远不要在控制器中访问 $element”的约定。

      我应该把它放入指令中,然后使用'ngKeydown'和$event来触发服务。

      但我觉得服务还不错,我会尽快返工控制器。


      更新:

      似乎 'ng-keydown' 只适用于输入标签。

      所以我只是写了一个指令并注入$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);
               }
             })
           }
         }
        })
      

      这样更好。

      【讨论】:

        【解决方案9】:

        请查看来自 ng-newsletter.com 背后的人的 example;查看 their tutorial 创建 2048 游戏,它有一些不错的代码,使用键盘事件服务。

        【讨论】:

          【解决方案10】:

          下面让您在控制器中编写所有快捷方式逻辑,该指令将处理其他所有事情。

          指令

          .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());
                  }
              };
          }])
          

          控制器

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

          HTML

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

          【讨论】:

            【解决方案11】:

            你可以试试这个库,它使管理热键变得非常容易,它会在你浏览应用程序时自动绑定和取消绑定键

            angular-hotkeys

            【讨论】:

              【解决方案12】:

              我不知道这是否是一种真正的角度方式,但我做了什么

              $(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>
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2022-12-09
                • 1970-01-01
                • 1970-01-01
                • 2011-10-25
                • 1970-01-01
                • 2016-01-13
                • 1970-01-01
                相关资源
                最近更新 更多