-
-
Save EpokK/5884263 to your computer and use it in GitHub Desktop.
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(); | |
} | |
}); | |
}; | |
}); |
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();
}
});
};
});
Thanks! I just plugged it in and it worked.
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 :)
Thanks!
👍 @EpokK
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();
}
});
};
});
Thanks
"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.
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
EpokK, would you license this function under a permissive license, like MIT? Thanks!
nice
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();
});
});
This works great. You're awesome!!!
👍
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);
}
});
};
}
};
}];
}
);
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();
}
});
}
};
});
No was is asking the question if element.on("keydown keypress", fun)
should be unbind? when destroying the element?
@jose-marin thanks for the snippet!
attrs is no needed.
link: function(scope, element, attrs) {
@jbaroudi thanks for typescript users
@jose-marin - nicely done. Thanks
Works like a charm.
Thanx.