4

I'm new to Angular, help will be much appreciated. I'm making an app which has different areas, let's call it pages (even though it's truly a 1 page app) for which I naturally use different views, and I have a common layout with the body, styles and scripts etc.

I'm running into a problem in which I want to listen to keyboard events in only one of the pages of the app which is meant to be interactive while the others are administrative. I can bind keyboard events to inputs or to the document or body. Input is not suitable and my document and body are global and I don't want to listen for every single keypress in my app.

What am I supposed to do to solve this problem in Angular?

My code is here: https://github.com/mgenev/geminiFc/blob/master/public/js/controllers/practice.js

I cheated with jQuery and bound the event to the body in the controller for the particular page, but Angular wasn't reacting like an event happened.

$('body').keydown(function(e) {
            $scope.changeIndex(e);
});

Then I read I have to use $scope.$apply(); which I did in the bottom of the changeIndex function which the event fires.

This actually worked, but when I call the changeIndex through a click event which is the alternative way of controlling my UI

   <div class="practice-controls-bottom">
        <i ng-click="changeIndex('down');" class="icon-thumbs-down icon-4x thumbs-down"></i>
        <i ng-click="changeIndex('up');" class="icon-thumbs-up icon-4x thumbs-up pull-right"></i>
    </div>

Angular gives me an error:

Error: $apply already in progress
    at Error (<anonymous>)
    at g (http://localhost:3000/lib/angular/angular.min.js:85:37)
    at Object.e.$apply (http://localhost:3000/lib/angular/angular.min.js:89:129)
    at Object.$scope.changeIndex (http://localhost:3000/js/controllers/practice.js:173:20)

Looking forward to some advice. Thanks!

2 Answers 2

5

You can try either one of the following solutions

Call the $scope.$apply in the keydown handler instead of changeindex method

$('body').keydown(function (e) {
    $scope.$apply(function () {
        $scope.changeIndex(e);
    })
});

or check whether the code is running within a apply/digest cycle before calling $apply in changeIndex again like

if(!$scope.$$phase){
    $scope.$apply()
}
Sign up to request clarification or add additional context in comments.

1 Comment

Your last code block were you check if a digest cycle is running works like a charm. Small typo though: $scope.$apply()
2

Did you have a look at the ng-keypress, ng-keyup, ng-keydown directives? That way, you could only set the directive on the element inside the view you have to listen to keypresses.

Doc: http://docs.angularjs.org/api/ng.directive:ngKeypress

EDIT: Add tabindex="0" to enable key events on any element.

Here is a working demo: http://plnkr.co/edit/whXgmQU1pKjuRqvokC2Z

html

<div ng-keypress="changeIndex($event)" tabindex="0">Something</div>

js

app.controller('MyCtrl', [function($scope) {
    $scope.changeIndex = function($event) {
        // $event.keyCode...
    }
}]); 

There is also a directive in Angular-UI http://angular-ui.github.io/ui-utils/ that let you associate key combinaisons with functions.

<div ui-keypress="{13:'changeIndex($event)'}"></div>

14 Comments

thanks, i did try this, but i didn't have any luck binding keypress to divs, i even tried to force a focus on it to see if it works, but no luck
The reason is that you must add tabindex="0" to the div in order to enable key events! It works for any element.
i will try that, will be superb if it works, thanks!
You can have a look at the demo and see it working hopefully!
Nice! Glad I could help you.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.