Skip to content

Instantly share code, notes, and snippets.

@EpokK
Last active September 26, 2025 13:35
Show Gist options
  • Select an option

  • Save EpokK/5884263 to your computer and use it in GitHub Desktop.

Select an option

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();
}
});
};
});
@marcofranssen

Copy link
Copy Markdown

I added a the directive on a module, but somehow the code never executes. Put a breakpoint at the first line of code.

//app.js
angular.module('notify', [
    'notify.controllers',
    'notify.services',
    'notify.directives'
  ]);
//directives.js
angular.module('notify.directives', [])
    .directive('ngEnter', function () {
        return function (scope, elements, attrs) {
            elements.bind('keydown keypress', function (event) {
                if (event.which === 13) {
                    scope.$apply(function () {
                        scope.$eval(attrs.ngEnter);
                    });
                    event.preventDefault();
                }
            });
        };
    });
<button ng-enter="doAction(userId)" ng-click="doAction(userId)"></button>

What am I missing? The click is working!

@zbabtkis

Copy link
Copy Markdown

@marcofranssen You might need to specify the scope on the directive. I had the same problem and that fixed it. Mine looks like this:

module.directive('ngEnter', function () {
        return {
           controller: 'MyCtrl'
           link: function (scope, elements, attrs) {
              elements.bind('keydown keypress', function (event) {
                  if (event.which === 13) {
                      scope.$apply(function () {
                          scope.$eval(attrs.ngEnter);
                      });
                      event.preventDefault();
                  }
              });
           }
        };
    });

@tlambeir

Copy link
Copy Markdown

I wrote a simple test for using this in our angular project:

'use strict';

describe('Directive: ngEnter', function () {

  // load the directive's module
  beforeEach(module(app));

  var element,
      scope;

  beforeEach(inject(function ($rootScope) {

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

  }));

  /**
   * Compile the directive into HTML
   */
  function compileDirective(){
    element = angular.element('<input type="text" data-ng-enter="mockFunction()" />');
    inject(function($compile){
      element = $compile(element)(scope);
    });
    scope.$apply();
  }

  it('it should call the mock function on pressing enter', function () {
    spyOn(scope,'mockFunction');
    var e = jQuery.Event('keypress');
    e.which = 13; //choose the one you want
    e.keyCode = 13;
    element.trigger(e);
    expect(scope.mockFunction).toHaveBeenCalled();
  });

});

@jobsamuel

Copy link
Copy Markdown

Thanks for this EpokK, it's really helpful!

@jeffmcmahan

Copy link
Copy Markdown

Thanks for this, EpokK!

@sachitsac

Copy link
Copy Markdown

Hi, Thanks for this directive. I just tried to pass $event to a function that was invoked as follows:

<div ng-enter="doSomething(a, b, $event)"></div>

But this did not pass the $event to the custom function i was trying to invoke. Now i checked angularjs inplementation of ng{Events} and found we need to eval $event as well. I fixed it using the code below but i am not sure if this is the correct way. Can anyone here confirm ?

'use strict';

angular.module('myApp').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:event});
          });
        event.preventDefault();
      }
    });
  };
});

@jaumebonet

Copy link
Copy Markdown

Very nice!
It work like a charm for my purpose!

@abobwhite

Copy link
Copy Markdown

Thanks! Very helpful and worked like a charm.

@adswebwork

Copy link
Copy Markdown

Works like a charm.
Thanx.

@technotrom

Copy link
Copy Markdown

it doesn't work for me.

//html
<input type="text"
ng-model="Search"
ng-enter='alert(Search)'
class="form-control"

         >

//javascript

angular.module('ui.bootstrap').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': event});
});

                event.preventDefault();
            }
        });
    };
});

@japo32

japo32 commented Jun 3, 2015

Copy link
Copy Markdown

Thanks! I just plugged it in and it worked.

@jbaroudi

Copy link
Copy Markdown

typescript version if you like

module util {
'use strict';

export class KeyEnterDirective implements angular.IDirective {

    public link;

    restrict = 'A';
    scope = false;

    constructor() {
        this.link = this.unboundLink.bind(this);
    }

    unboundLink(scope: angular.IScope, element: JQuery, attributes: any) {
        element.bind('keydown keypress', function(event: JQueryEventObject) {
            if (event.which === 13) {
                scope.$apply(function () {
                    scope.$eval(attributes.keyEnter);
                });

                event.preventDefault();
            }
        });
    }

    static instance(): ng.IDirectiveFactory {

        var directive = () => new KeyEnterDirective();
        directive.$inject = [];
        return directive;
    }

}
}

and it's loaded like this:

angular.module('myModule', ['ngAnimate', ...])
    .directive('keyEnter', util.KeyEnterDirective.instance())

You can change the directive name ('keyEnter') to decorate your attribute:

<input key-enter="myFunction()" />

Hope this helps someone :)

@Anhvutpbn

Copy link
Copy Markdown

Thanks!

@mittalabhas1

Copy link
Copy Markdown

👍 @EpokK

@samuelneff

Copy link
Copy Markdown

Minor improvement to support ng-enter with no value to trigger the ng-click on the same element, which for me is usually what I want.

'use strict';

angular.module('myApp').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 || attrs.ngClick, {$event:event});
          });
        event.preventDefault();
      }
    });
  };
});

@charleswangyi

Copy link
Copy Markdown

Thanks

@sankety

sankety commented Oct 30, 2015

Copy link
Copy Markdown

"Error: $apply already in progress"
I got this error in console and some how the event.preventDefault(); isn't working in my case
the cursor goes to next line.

@sankety

sankety commented Oct 30, 2015

Copy link
Copy Markdown

routerApp.directive('ngEnter', function () {
return function (scope, element, attrs) {
element.bind("keypress", function (event) {
//element.bind("keydown keypress", function (event) {
if(event.which === 13) {
scope.$apply(function (){
scope.$eval(attrs.ngEnter);
});

            event.preventDefault();
        }
    });
};

});

I removed "Keydown" event and error disappeared

@rpalazzo

Copy link
Copy Markdown

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

@alwayrun

Copy link
Copy Markdown

nice

@cafesanu

Copy link
Copy Markdown

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

caviles commented Apr 2, 2016

Copy link
Copy Markdown

This works great. You're awesome!!!

@sgsheg

sgsheg commented Aug 1, 2016

Copy link
Copy Markdown

👍

@dougrchamberlain

Copy link
Copy Markdown

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

jose-marin commented Aug 11, 2016

Copy link
Copy Markdown

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
Copy Markdown

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

@georgeportillo

georgeportillo commented Sep 21, 2016

Copy link
Copy Markdown

@jose-marin thanks for the snippet!

@michalpanek

Copy link
Copy Markdown

attrs is no needed.

  link: function(scope, element, attrs) {

@w0nderw0man

Copy link
Copy Markdown

@jbaroudi thanks for typescript users

@developerant

Copy link
Copy Markdown

@jose-marin - nicely done. Thanks

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