Skip to content

Instantly share code, notes, and snippets.

@EpokK
Last active January 7, 2022 13:57
Show Gist options
  • Save EpokK/5884263 to your computer and use it in GitHub Desktop.
Save EpokK/5884263 to your computer and use it in GitHub Desktop.
ngEnter directive if you can use submit form(https://twitter.com/ririlepanda)
app.directive('ngEnter', function() {
return function(scope, element, attrs) {
element.bind("keydown keypress", function(event) {
if(event.which === 13) {
scope.$apply(function(){
scope.$eval(attrs.ngEnter);
});
event.preventDefault();
}
});
};
});
@rpalazzo
Copy link

EpokK, would you license this function under a permissive license, like MIT? Thanks!

@alwayrun
Copy link

nice

@cafesanu
Copy link

even better, why not make it more generic to support any key press (ng-keypress does not support all keys... like escape) or an specific key (specified as an attribute)...

// Directive
angular.module('myApp').directive('ngOnKeyPress', function () {
    return function(scope, element, attrs) {
        var validKeyCodes = {
            esc: 27,
            enter: 13
        };

        element.bind('keydown keypress', function(event) {
            if (!attrs.key || event.which === validKeyCodes[attrs.key]) {
                scope.$apply(function() {
                    scope.$eval(attrs.hbOnKeyPress, {$event: event});
                });
                event.preventDefault();
            }
        });
    };
});

// Directive test
describe('Directive: ng-on-key-press', function() {
    var element,
        scope,
        validKeyCodes = {
            esc: 27,
            enter: 13
        };

    beforeEach(module(app));

    beforeEach(inject(function($injector) {
        var $rootScope = $injector.get('$rootScope');

        scope = $rootScope.$new();
        scope.mockFunction = function() {};
    }));

    /**
     * Compile the directive into HTML
     */
    function compileDirective(html) {
        element = angular.element(html);
        inject(function($compile) {
            element = $compile(element)(scope);
        });
        scope.$apply();
    }

    it('it should call the mock function on pressing enter', function() {
        var eventMock = angular.element.Event('keypress');

        compileDirective('<input type="text" ng-on-key-press="mockFunction()" />');

        spyOn(scope, 'mockFunction');
        element.trigger(eventMock);
        expect(scope.mockFunction).toHaveBeenCalled();
    });

    it('it should call the mock function on pressing a key that does match key specified', function() {
        var eventMock = angular.element.Event('keypress');

        compileDirective('<input type="text" ng-on-key-press="mockFunction()" key="esc" />');
        spyOn(scope, 'mockFunction');
        eventMock.which = validKeyCodes.enter;
        eventMock.keyCode = validKeyCodes.enter;
        element.trigger(eventMock);
        expect(scope.mockFunction).not.toHaveBeenCalled();
    });

    it('it should not call the mock function on pressing a key that does NOT match key specified', function() {
        var eventMock = angular.element.Event('keypress');

        compileDirective('<input type="text" ng-on-key-press="mockFunction()" key="esc" />');
        spyOn(scope, 'mockFunction');
        eventMock.which = validKeyCodes.esc;
        eventMock.keyCode = validKeyCodes.esc;
        element.trigger(eventMock);
        expect(scope.mockFunction).toHaveBeenCalled();
    });

    it('it should not call the mock function on pressing a key when wrong key specified', function() {
        var eventMock = angular.element.Event('keypress');

        compileDirective('<input type="text" ng-on-key-press="mockFunction()" key="doesnotexist" />');
        spyOn(scope, 'mockFunction');
        eventMock.which = validKeyCodes.enter;
        eventMock.keyCode = validKeyCodes.enter;
        element.trigger(eventMock);
        expect(scope.mockFunction).not.toHaveBeenCalled();
    });
});

@caviles
Copy link

caviles commented Apr 2, 2016

This works great. You're awesome!!!

@sgsheg
Copy link

sgsheg commented Aug 1, 2016

👍

@dougrchamberlain
Copy link

This is silly. angular provides ng-keypress. what functionality is this providing that ng-keypress doesn't already? This directive has been around since at least 1.2.29 https://code.angularjs.org/1.2.29/docs/api/ng/directive/ngKeypress I looked at the source code too, it's

/*
               * A collection of directives that allows creation of custom event handlers that are defined as
               * angular expressions and are compiled and executed within the current scope.
               */
              var ngEventDirectives = {};

              // For events that might fire synchronously during DOM manipulation
              // we need to execute their event handlers asynchronously using $evalAsync,
              // so that they are not executed in an inconsistent state.
              var forceAsyncEvents = {
                'blur': true,
                'focus': true
              };
              forEach(
                'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
                function(eventName) {
                  var directiveName = directiveNormalize('ng-' + eventName);
                  ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
                    return {
                      restrict: 'A',
                      compile: function($element, attr) {
                        // We expose the powerful $event object on the scope that provides access to the Window,
                        // etc. that isn't protected by the fast paths in $parse.  We explicitly request better
                        // checks at the cost of speed since event handler expressions are not executed as
                        // frequently as regular change detection.
                        var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
                        return function ngEventHandler(scope, element) {
                          element.on(eventName, function(event) {
                            var callback = function() {
                              fn(scope, {$event:event});
                            };
                            if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
                              scope.$evalAsync(callback);
                            } else {
                              scope.$apply(callback);
                            }
                          });
                        };
                      }
                    };
                  }];
                }
              );

@jose-marin
Copy link

jose-marin commented Aug 11, 2016

You don't need to use eval if you bind the function passed. It also makes the scope isolate, so the directive doesn't depend on a parent controller.

app.directive('onEnter', function() {
    return {
        restrict: "A",
        scope: {
            action: "&onEnter"
        },
        link: function(scope, element, attrs) {
            element.on("keydown keypress", function(event) {
                if(event.which === 13) {
                    scope.$apply(scope.action);
                    event.preventDefault();
                }
            });
        }
    };
});

@adilbenmoussa
Copy link

No was is asking the question if element.on("keydown keypress", fun) should be unbind? when destroying the element?

@georgeportillo
Copy link

georgeportillo commented Sep 21, 2016

@jose-marin thanks for the snippet!

@michalpanek
Copy link

attrs is no needed.

  link: function(scope, element, attrs) {

@w0nderw0man
Copy link

@jbaroudi thanks for typescript users

@developerant
Copy link

@jose-marin - nicely done. Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment