Що таке AngularJS спосіб створення глобальних комбінацій клавіш?


77

Я вважаю, що я повинен використовувати директиву, але здається дивним додавати директиву до тіла, але слухати події в документі.

Як правильно це зробити?

ОНОВЛЕННЯ: Знайшов інтерфейс AngularJS та побачив їх реалізацію директиви щодо натискання клавіш.


1
Припускаю, ви маєте на увазі комбінації клавіш ... Мені це теж цікаво, і я приходжу до висновку, що angular - не найкращий інструмент для цього завдання. Я написав директиву, яка робить це, але є проблеми - по-перше, це семантична, на яку ви також натякаєте, також я не думаю, що вважається гарною практикою обертати jquery в директиву, і це призвело до деяких заплутаних ситуацій, коли є декількома шаблонами, лише деякі з яких потребують ярликів документа.
Джейсон

Ярлики потрібно підключати до мого контролера. І я не бачу жодних переваг зовнішнього модуля jquery. Також я бачу два можливі способи: 1) модуль зовнішніх ярликів jQuery + зв'язок pubsub з контролером. 2) Директива Angular, що дивно, але, гадаю, нормально надавати функцію посилання за допомогою ярликів.
ValeriiVasin

Я не думаю, що ви могли б додати директиви angularjs ui до документа, вони скопіюються до елемента.
Джейсон

3
не потрібна додаткова бібліотека ... скористайтеся $document.bind('keypress') Див. документ $
charlietfl

2
Зараз посилання 404. Якщо оновлене місцезнаходження, будь ласка, можете оновити його.
HockeyJ

Відповіді:


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>

Демонстрація Plunker


Дякую за відповідь. Я бачу, що ви вважаєте, що ми повинні робити це як директиву.
Валерій Васін

посилання: функція postLink (scope, iElement, iAttrs) {window.addEventListener ('load', function (e) {scope. $ apply (scope.keyPress (e));}, false); }
devnill

дивно мати директиву щодо цього, але не сервіс. директива - багаторазовий компонент інтерфейсу користувача (у більшості випадків).
ses

Кожного разу, коли я чую про JQuery всередині Angular, у мене з’являються мурашки
валокс

69

Я б сказав, що більш правильним способом (або "кутовим способом") було б додати його до директиви. Ось простий, який допоможе вам розпочати роботу (просто додайте 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
Шлях, справді чистий.
Anthony Perot

2
Це було прив'язуванням до документа $, а не до елемента, який працював у мене для отримання ключових подій у div. +1 для показу, як також вводити документ $.
прототип

6
Згідно з наведеним вище кодом ця директива прив'язує подію до елемента window.document ($ document), хоча її можна приєднати до будь-якого тегу DOM, а не лише <body>, оскільки перевірки немає. У цьому випадку елемент, до якого приєднана директива, може бути знищений, але прив'язаний прослуховувач подій залишиться. Я рекомендую або помістити туди якусь перевірку (щоб обмежити елемент <body>), або реалізувати метод, який розв'яже прослуховувач подій за допомогою $ scope.on ('знищити').
chmurson

1
Чому він не захоплює Escapeключ?
Саїд Неамати

@SaeedNeamati Див. Відповідь Єгуди Каца на Звільнення з цього приводу 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

1
Вам потрібно застосувати зміну var that = this; $document.bind("keydown", function(event) { $scope.$apply(function(){ that.handleKeyDown(event); });
FreshPow

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

Оновлення: Натомість я зараз використовую гарячі клавіші .


Відмінна скрипка, але чи можемо ми пов’язати всі ярлики в директиві, щоб я міг телефонувати по всьому своєму додатку?
Приянка Павар

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=preview


Як послуга

Перетворити цю директиву на послугу дуже просто. Єдина реальна різниця полягає в тому, що сфера дії не піддається сервісу. Щоб запустити дайджест, ви можете ввести $rootScopeабо використовувати a $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

    Другий забезпечує трохи більше можливостей та гнучкості, тобто автоматичну згенеровану шпаргалку для гарячих клавіш для вашого додатка.

Оновлення : рішення 3 більше не доступне в Angular ui.

  1. Окрім наведених вище рішень, існує ще одна реалізація, виконана командою angularui. Але недоліком є ​​те, що рішення залежить від JQuery lib, що не є тенденцією у кутовій спільноті. (Спільнота Angular намагається просто використовувати jqLite, який поставляється з angularjs, і уникнути надмірних залежностей.) Ось посилання

    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
Дякую, що поділились. Я проголосував. Насправді кутова гаряча клавіша chieffancypants виглядає приголомшливо, але не знаю, як налаштувати її лише під конкретну модель. гарячі клавіші drahak добре працюють для стосунків один до одного!
Learner_Programmer

Я думаю, це третє рішення (ui-utils) більше не підтримується або посилання недійсне. Ретпо-репозиторій github позначений як застарілий
Бернардо Рамос

Дякую за коментар Бернардо, я видалю третє рішення.
Тім Хонг

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

Це працює, але ви можете помітити, що я ввожу $ елемент і $ документ в контролер.

Це погана практика контролера і порушує конвенцію `` Ніколи не отримувати доступ до елемента $ у контролері ''

Я повинен вкласти це в директиву, а потім використовувати 'ngKeydown' і $ event для запуску служби.

Але я думаю, що послуга чудова, і я перероблю контролер швидше.


оновлено:

Здається, "ng-keydown" працює лише у вхідних тегах.

Тому я просто пишу директиву і ввожу $ документ:

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

Перевірте цей приклад у хлопців behid ng-newsletter.com; перегляньте їх підручник зі створення гри 2048 року, у ньому є приємний код за допомогою сервісу для клавіатурних подій.


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

Ви можете спробувати цю бібліотеку, це дозволило дуже легко керувати гарячими клавішами, вона автоматично прив'язує та відв'язує клавіші під час навігації по додатку

кутові гарячі клавіші


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>
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.