【问题标题】:How to watch for a keypress combination in Angularjs? [duplicate]如何在 Angularjs 中观察按键组合? [复制]
【发布时间】:2013-11-11 16:36:55
【问题描述】:

我正试图让我的控制器监视按键组合。为了论证起见,假设:上上下下左右左右b a。无论用户当前在页面的哪个位置,我如何才能有角度地注意这些?

【问题讨论】:

  • 如果你想用keyPress上的组合键触发代码(例如Ctrl + a),看看这个(使用AngularJS工厂):jsfiddle.net/firehist/nzUBg
  • 记住你couldif (e.ctrlKey && e.which === number)

标签: angularjs keypress


【解决方案1】:

看来您可以使用ng-keydown 来执行此操作。

这是working plunker

对于这个示例,我只是将ng-keydown 绑定到<body>。可以很好地捕获全局的所有键盘事件。

正如@charlietfl 指出的那样,ng-keydown 注册了很多键盘事件,因此要使其可用需要做很多工作。例如,如果您尝试侦听组合(如ctrl + r),则ctrl 键将注册多次。

JS:

var myApp = angular.module('myApp', []);

myApp.controller('Ctrl', function($scope) {


    $scope.keyBuffer = [];

    function arrays_equal(a,b) { return !(a<b || b<a); }

    $scope.down = function(e) {

      $scope.keyBuffer.push(e.keyCode);

      var upUp = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65];
      if (arrays_equal(upUp, $scope.keyBuffer)) {

        alert('thats it!');
      }
    };

  });

HTML:

<body ng-controller="Ctrl" ng-keydown="down($event)">

【讨论】:

  • 正在玩这个,但必须检查用户的顺序中断,如果它中断则为空数组,在推送新键之前索引现有数组...开始变得耗时,使其防弹
  • @charlietfl 是的,完全正确。因为如果您按住键,ng-keydown 会重复。直到现在我才检查过这个。也许ng-keyup 会更好?
  • 一旦你深入了解它就会有更多......
  • 这行得通,我还添加了缓冲区功能,因此 keyBuffer 永远不会比 upUp 长。正如 Kos Prov 在此线程的另一个答案中提到的那样,它确实有一个缺点,即每次按键都会调用摘要。
【解决方案2】:

我正在使用不同的方式来做到这一点。

$scope.keyboard = {
  buffer: [],
  detectCombination: function() {
    var codes = {};

    this.buffer.forEach(function(code) {
      codes['key_' + code] = 1;
    });

    if ((codes.key_91 || codes.key_93) && codes.key_8) {
      // I'm looking for 'command + delete'
    }
  },
  keydown: function($event) {
    this.buffer.push($event.keyCode);
    this.detectCombination();
  },
  keyup: function($event, week) {
    this.buffer = [];
  }
};

【讨论】:

    【解决方案3】:

    检测 Backspace-Key (Mac) 和 Del-Key (PC):

    <body ng-controller="Ctrl" ng-keydown="keyDown($event)">..<body>
    
    $scope.keyDown = function(value){
        if(value.keyCode == 46 || value.keyCode == 8) {
            //alert('Delete Key Pressed');
        }
    };
    

    【讨论】:

    • 这如何回答这个问题? OP询问按键组合
    【解决方案4】:

    这都是未经测试的,但你可以使用ng-keypress

    <body ng-keypress="logKeys($rootScope,$event)">...</body>
    

    调用类似的函数:

    appCtrl.$scope.logKeys = function($rootScope,$event){
        $rootScope.keyLog.shift(); // Remove First Item of Array
        $rootScope.keyLog.push($event.keyCode); // Adds new key press to end of Array
        if($scope.$rootScope.keyLog[0] !== 38) { return false; } // 38 == up key
        if($scope.$rootScope.keyLog[1] !== 38) { return false; }
        if($scope.$rootScope.keyLog[2] !== 40) { return false; } // 40 = down key
        if($scope.$rootScope.keyLog[3] !== 40) { return false; }
        if($scope.$rootScope.keyLog[4] !== 27) { return false; } // 37 = left key
        if($scope.$rootScope.keyLog[5] !== 39) { return false; } // 39 = right key
        if($scope.$rootScope.keyLog[6] !== 37) { return false; }
        if($scope.$rootScope.keyLog[7] !== 39) { return false; }
        if($scope.$rootScope.keyLog[8] !== 65) { return false; } // 65 = a
        if($scope.$rootScope.keyLog[9] !== 66) { return false; } // 66 = b
    
        $rootScope.doThisWhenAllKeysPressed(); // Got this far, must all match!
        return true;
    }
    

    在输入字段之外,我认为 ng-keypress 不起作用,但来自 angular-ui 的按键可能会。

    我确定也应该有一个数组差异函数,但具体的调用现在回避了我。

    【讨论】:

    • keypress 不会为箭头键触发。
    • angular 让你可以访问 $document,只需注入它......可以绑定到那个
    【解决方案5】:

    这是我的看法:

    var app = angular.module('contra', []);
    app.directive('code', function () {
        function codeState() {
            this.currentState = 0;
            this.keys = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65];
            this.keyPressed = function (key) {
                if (this.keys[this.currentState] == key) {
                    this.currentState++;
                    if (this.currentState == this.keys.length) {
                        this.currentState = 0;
                        return true;
                    }
                } else {
                    this.currentState = 0;
                }
                return false;
            };
        };
        return {
            restrict: 'A',
            link: function (scope, element, attrs) {
                var cs = new codeState();
                scope.isValid = "NO";
                element.bind("keydown", function (event) {
                    scope.$apply(function () {
                        if (cs.keyPressed(event.which)) {
                            scope.isValid = "YES";
                            console.log("CODE ENTERED");
                        } else {
                            scope.isValid = "NO";
                        }
                    });
                });
            }
        }
    });
    

    它的不同之处在于它是一个指令,所以如果你将它附加在正文上,它将应用于整个页面。这也允许多次输入代码。

    Plunkr:

    http://plnkr.co/edit/tISvsjYKYDrSvA8pu2St

    【讨论】:

    • plnkr 有效吗?我将此添加到我的应用程序指令中,它绑定到它,但我不知道如何触发它。当我尝试 plnkr 时,它只是向示例报告 NO。我做错了吗?
    【解决方案6】:

    查看this plunker。我已经实现了一个简单的“连续 2 次向上击键”场景。

    您可以使用纯 jQuery 来完成,并使用 $rootScope.$broadcast 传达事件。

    在 Angular run 回调中注册 jQuery 代码(保证 Angular 已经引导):

    app.run(function($rootScope) {
      var upHitOnce = false;
      $(document).keyup(function(event) {
        if (event.which == 38) {
          if (upHitOnce) {
            $rootScope.$broadcast('DoubleUpFired');
            $rootScope.$apply();
            upHitOnce = false;
          } else {
            upHitOnce = true;
          }
        } else {
          upHitOnce = false;
        }
      });
    });
    

    然后任何控制器都可以像这样监听这个事件:

    $scope.$on('DoubleUpFired', function() {
        $scope.fired = true;
    });
    

    ng-keydown 操作回调绑定到body 是可以的,但有一个小缺点。它会在每次击键时触发$digest。您真正想要的是$digest,仅当您以某种方式需要更新 UI 时输入序列。

    编辑

    请参阅 cmets 了解如何删除实际的 jQuery 依赖项。

    【讨论】:

    • 我喜欢它只在输入序列时才调用digest,但是有没有办法在不显式使用jQuery的情况下做到这一点?
    • jQuery 是可选的。 Angular 内置了一个 jqLit​​e 实现。只需将$(document).keyup(function(event) { ... } 替换为angular.element(document).bind('keyup', function(event) { ... }
    • 说实话,即使$rootScope.$apply() 也是一种浪费。它为整个范围层次结构启动$digest。您可以触发该事件,并且每个侦听它的范围都可以对其自身显式调用$digest()。这样,只有页面中感兴趣的部分会被重新渲染。看看here。当然,没有人能够区分这两种方式,开发人员很容易忘记它。
    【解决方案7】:

    如果您尝试使用 'ctrl+s' 或 'commond+s'(更改 commondKey)进行保存,也许可以像这样使用:

    指令:

    (function () {
    
      'use strict';
      var lastKey = 0;
      //var commondKey = 17;
      var commondKey = 91;
      angular
        .module('xinshu')
        .directive('saveEnter', function () {
          return function (scope, element, attrs) {
            element.bind("keydown", function (event) {
              if (event.which != commondKey && event.which != 83) {
                lastKey = 0;
              }
              if (lastKey == commondKey && event.which == 83) {
                scope.$apply(function () {
                  scope.$eval(attrs.saveEnter);
                });
                event.preventDefault();
              }
              lastKey = event.which;
            });
          };
        });
    })();
    

    元素:

    <input id="title" save-enter="vm.saveTitle()"/>
    

    您可以重命名 saveEnter in 指令,并更改 html 中的 save-enter。

    'vm.saveTitle()' 是你想做的事情。

    【讨论】:

      猜你喜欢
      • 2017-08-30
      • 2013-01-20
      • 1970-01-01
      • 2017-12-03
      • 1970-01-01
      • 2018-04-02
      • 1970-01-01
      • 2017-08-21
      • 1970-01-01
      相关资源
      最近更新 更多