创建全局键盘快捷键的AngularJS方法是什么?


77

我想我应该使用指令,但是将指令添加到主体似乎很奇怪,但是侦听文档中的事件。

什么是正确的方法?

更新:找到了AngularJS UI,并看到了它们对keypress指令实现。


1
我以为您是指键盘快捷键...我也对此感到很好奇,我得出的结论是,角度并不是执行此任务的最佳工具。我写了一个指令来执行此操作,但是存在问题-首先是您所暗示的语义上的指令,而且我认为将jquery包装在指令中不是一种好习惯,并且在出现这种情况时会导致一些令人困惑的情况是多个模板,只有其中一些需要文档快捷方式。
杰森

快捷方式需要与我的控制器连接。而且我没有看到外部jquery模块的任何好处。我也看到了两种可能的方法:1)jQuery外部快捷方式模块+与控制器的pubsub通信。2)Angular指令,这很奇怪,但是我想可以为链接功能提供快捷方式是可以的。
ValeriiVasin

我认为您不能将angularjs ui指令添加到文档中,它们的范围仅限于一个元素。
杰森

3
不需要额外的库...使用$document.bind('keypress') 参见$ document
charlietfl 2013年

2
链接现在是404。如果有更新的位置,请更新它。
HockeyJ

Answers:


10

这就是我使用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>

柱塞演示


感谢您的回复。我认为您认为我们应该按照指示进行操作。
ValeriiVasin

链接:函数postLink(scope,iElement,iAttrs){window.addEventListener('load',function(e){scope。$ apply(scope.keyPressed(e));},false); }
devnill 2014年

对此有一个指令很奇怪,但没有服务。指令-可重用的ui组件(在大多数情况下)。
ses

每次我在Angular中听到有关JQuery的消息时,都会碰到
鸡皮

69

我会说一种更合适的方法(或“ 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>'
    };
  }
]);

2
要走的路,真的很干净。
安东尼·佩罗

2
它绑定到$ document而不是元素上,该元素对我有用以在div上获取关键事件。+1用于显示如何也注入$ document。
原型

6
根据上面的代码,此指令将事件绑定到window.document($ document)元素,同时它可以附加到任何DOM标记,不仅是<body>,因为没有验证。在这种情况下,可以删除附加有指令的元素,但绑定的事件侦听器将保留。我建议要么放置一些验证(将元素限制为<body>),要么实现使用$ scope.on('destroy')取消绑定事件监听器的方法。
chmurson

1
为什么不捕获Escape密钥?
Saeed Neamati 2015年

@SaeedNeamati参见耶胡达·卡兹(Yehuda Katz)对Resig的回复ejohn.org/blog/keypress-in-safari-31
jmagnusson

27

用途$document.bind

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

似乎有两种方法,一种是创建指令并通过$event函数将其传递给控制器,另一种是将事件直接绑定到控制器中。控制器方法似乎更少的代码,并且结果相同。是否有理由选择一种方法而不是另一种方法?
Nucleon

这种方法每次按键给我多个事件触发器,我使用了捕鼠器而不是$ document.bind,这似乎已经足够了。
rwheadon

这种方法禁用了其他变量{{abc}}的自动更新
windmaomao 2015年

1
您需要应用更改 var that = this; $document.bind("keydown", function(event) { $scope.$apply(function(){ that.handleKeyDown(event); });
FreshPow's

20

我还不能保证,但是我开始看一下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);

10

这是一个用于键盘快捷键的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');
    });
}

更新:我现在使用的是角度热键


很棒的小提琴,但是我们可以将所有快捷方式绑定到指令中,以便我可以在我的应用程序中调用吗?
Priyanka Pawar

7

作为指令

本质上,这是在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...          
      });
    }
  };
}

使用键盘指令插入

http://plnkr.co/edit/C61Gnn?p=预览


作为服务

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


4

稍短的答案只是看下面的解决方案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社区的趋势。(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,它避免了我遇到的许多小怪问题。


1
感谢分享。我已经投票了。实际上,fantfancypant的角度热键看起来很棒,但不知道如何仅针对特定模型对其进行自定义。drahak的角度热键适用于一对一的关系!
Learner_Programmer 2014年

我猜这第三个解决方案(ui-utils)已不再维护或链接无效。github存储库已标记为已弃用
Bernardo Ramos

感谢您的评论Bernardo,我将删除第三个解决方案。
添·洪

1

我提供了快捷方式服务。

看起来像:

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

好多了



0

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

指示

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


0

我不知道这是否是真正的成角度的方法,但是我做了什么

$(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>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.