Search code examples
javascriptangularjskeydownonkeydown

why $document.on('keydown') is one late?


I have a controller that injects $document to catch keydown event. But this event is always one keypress late.

I have another method by using $broadcast from ng-keydown in <body> that works fine.

My question is : what is wrong with $document.on('keydown', ...)?

Here is a working example :

<body ng-app="app" ng-keydown="$broadcast('mykeydown', $event);">
   <div ng-controller="BroadcastController">
     key down by broadcast: {{keycode}}
   </div>
   <div ng-controller="KeyFromDocController">
     key down from $document : {{keycode}} <br>[always one late !!!]
   </div>
</body>

<script>
  angular.module('app', [])
  .controller('BroadcastController', function($scope) {
    $scope.keycode = '?';
    $scope.$on('mykeydown',function(msg,evt) {
      $scope.keycode = evt.which;
    });
  })
  .controller('KeyFromDocController', function($scope,$document) {
    $scope.keycode = '?';
    $document.on('keydown',function(evt) {
      $scope.keycode = evt.which;
    });
  });
</script>

that you can test here http://plnkr.co/edit/omhTiLi31BnEwrSwk75A


Solution

  • If you did this, which you shouldn't, it wouldn't be "one late" as you say:

      $document.on('keydown',function(evt) {
        $scope.keycode = evt.which;
        $scope.$apply();
      });
    

    Example

    In Angular you shouldn't be doing these kind of operations from a controler, but from a directive, DOM manipulation and DOM events belong to the directives, concretely to the link function of the directive.

    What happens when you register an event like that is that you are registering the event inside the controller, but when the event get's triggered angular doesn't know anything about that function, so you would have to call $apply in order to trigger the $diggest cycle that will re-evaluate and refresh the expressions of your view.