Prevent form submission on enter key - angularjs

How can I prevent the enter key from submitting the form in angular?
Is there a way to catch the 13 key and disable it or set the form as invalid unless submitting from a button with ID of x?
Thanks

Since you have ng-click anyways, you could also use <button type="button">, even inside the form tag. The default behaviour of the button element is type="submit", which is what you want to prevent. So, no javascript needed at all!

Other users have already written that [button type="submit"] will cause this trouble. PLEASE NOTE: buttons WITHOUT any type="..." declaration are "submit" by default! So make sure you always use type="button".

After a couple hours, this weird code was the only thing that worked.
I'm waiting for better answers, won't accept this monster:
app.directive('onKeyup', function() {
return function(scope, elm, attrs) {
var allowedKeys = scope.$eval(attrs.keys);
elm.bind('keydown', function(evt) {
angular.forEach(allowedKeys, function(key) {
if (key == evt.which) {
evt.preventDefault(); // Doesn't work at all
window.stop(); // Works in all browsers but IE
document.execCommand("Stop"); // Works in IE
return false; // Don't even know why it's here. Does nothing.
}
});
});
};
});
and trigger it by using this on all form inputs:
<input on-keyup="bla" keys="[13]" .... />
For now, whenever the user press the enter key, the window try to submit, then fail to do so, not so silently. Ugly but it works.
Edit: keydown is a little better than keyup for the element bind, now enter key fails silently-ish

so simple, doesn't need to do anything. just add this to your form tag if you are using angular +2
<form (keydown.enter)="$event.preventDefault()" ...>

If you are attempting to prevent the form from being submitted on just a single element, you can add the following ng-keypress handler (this is for Angular 1.x):
<input type="text" name="myField" ng-keypress="keyPressHandler($event)"/>
With the following implementation for keyPressHandler:
$scope.keyPressHandler = function(e) {
if (e.keyCode === 13) {
e.preventDefault();
e.stopPropagation();
// Perform your custom logic here if any
}
}

I had a similar problem, I ended up taking the button out of the form.
Seeing as I use ng-click and everything is binded with ng-model it doesn't really matter if it's inside the form or not.
I realise this is bad practice but it sure as hell beats writing a custom directive to intercept keystrokes.

Check this:
if a form has 2+ input fields and no buttons or input[type=submit]
then hitting enter doesn't trigger submit
Thus if your form has 2+ input fields, you could use something like <span ng-click="submit()">Sumbit</span> to prevent key-trigger of enter key in those input fields.

I came across this issue. Yes, you would need to remove all type='submit' from your page, and make sure any other buttons have type="button" but then the challenge is still being able to use normal validation submission.
I created a directive that triggers form submission + form states for validation. Replacing:
<button type="submit">
with
<button submit-button type="button">
Directive:
export default /*#ngInject*/ function submitButton($log) {
return ({
require: '^form',
link: link,
restrict: 'A'
});
function link(scope, element, attributes, formCtrl) {
element.on('click', clickHandler);
function clickHandler() {
formCtrl.$setDirty(true);
formCtrl.$setSubmitted(true);
angular.element(element[0].form).triggerHandler('submit');
$log.info('Form Submitted');
}
}
You can still hit ENTER to submit when focused on your submit-button, better for UX and Accessibility I think.

The easiest solution to this I found is to use input type as button instead of submit and bind the form submit function with ng-click and not using the ng-submit in the form tag.
I hope this helps.

This is my weird but quick and simple solution without any directives.
HTML:
<form ng-submit='submitForm()'>
<input type='text'>
<button type='submit' ng-mousedown='doSubmit=true'>submit</button>
</form>
Controller:
$scope.submitForm = function() {
if (!$scope.doSubmit) {
return;
}
$scope.doSubmit = false;
console.log('execute some actions');
}

You can catch the default form submit in your controller using ng-submit on the form tag and it will prevent a submit:
http://docs.angularjs.org/api/ng.directive:ngSubmit
alternatively, if you really wanted to catch the key events, there are also directives for that which pass an event you can call stop:
http://docs.angularjs.org/api/ng.directive:ngKeyup

angular.element(document).ready(function () {
angular.element(window).keydown(function () {
if(event.keyCode == 13) {
event.preventDefault();
return false;
}
});
});
Try with this in angularjs controller

A form is submitted when the enter key is clicked while a control within the form has focus. If you register a listener using ng-submit you can intercept this and use prevent defaults to stop the default process (i.e. submitting the form). Have a look at th

The following should work . . . i.e., the form is only submitted on button click, and not on hitting Enter in the Input boxes. (This definitely works for reactive forms. I didn't test it for template forms).
<form #form [formGroup]="form" METHOD="GET" action="http://localhost:3000/test">
<input placeholder="Enter"/>
<input placeholder="The Dragon"/>
<button type="button" (click)="form.submit()">Submit</button>
</form>
Of course, remember all the imports and declarations:
app.module.ts
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
#NgModule({
imports: [
. . .
FormsModule,
ReactiveFormsModule
]
. . .
})
export class AppModule { }
test.component.ts
import { FormGroup, FormControl } from '#angular/forms';
#Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.scss']
})
export class TestComponent {
form: FormGroup = new FormGroup({});
constructor() { }
}

Try setting a variable when you click the submit button and checking that it has been set in the form submit.
$scope.click = function () {
$scope.clicked = true;
$scope.submit();
};
$scope.submit = function () {
if ($scope.clicked) {
... submit
} else {
... prevent defaults
}
$scope.clicked = false;
};
See jsfiddle

Related

How to prevent doubleclicking on a button with angularjs?

I want to select an element in my case a button on the page in my angularcontroller and then disable it. The button looks like this:
myBtn= $element.by.buttonText('submit')
I don't want the user to click the button twice in order to avoid to post requests in the backend. When I get the code above I get an angular reference order. What is an easy way to select a button and then set the disabled property to true so the user cannot click the button twice?
You can use ng-disabled to disable your button according to a flag set in your submit function. For example:
<form ng-submit="submit()">
...
<button type="submit" ng-disabled="isSubmitting">Submit</button>
</form>
and in your controller:
$scope.submit = function() {
$scope.isSubmitting = true;
$http.post('...').finally(function() {
$scope.isSubmitting = false;
});
};
if you have many buttons on page, then its better to create a directive so that on any button which is clickable, it doesnt get pressed twice
app.directive('ngClickDisable', function() {
return {
scope: {
clickAndDisable: '&'
},
link: function(scope, iElement, iAttrs) {
iElement.bind('click', function() {
iElement.prop('disabled',true);
scope.clickAndDisable().finally(function() {
iElement.prop('disabled',false);
})
});
}
};
});
This can be used on a button as follows:
<button ng-click-disable="functionThatReturnsPromise()">Click me</button>

How can I submit an angular-ui modal with keyboard

angular-ui modals (see http://angular-ui.github.io/bootstrap/ under Modal) can be default be cancelled by pressing escape. This option is configurable through the keyboard option to $modal.open().
I would also like to be able to submit such a modal by using the keyboard, for example by pressing Ctrl-Enter. (I have several different dialogs of this kind in my app. Each modal has a different controller due to different input / output requirements.)
The button specification at the end of the angular template of the example in the documentation looks like this:
<div class="modal-footer">
<button class="btn btn-primary" ng-click="ok()">OK</button>
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
</div>
The rest of the example can be found here: http://plnkr.co/edit/uNeRrPI8CdZFNslcQzFy?p=preview
Here's a gist for adding an extra angular directive for keyboard shortcuts to elements with ng-click directives: https://gist.github.com/mkhatib/5802718 -- is this the best solution?
You could listen for a key event in the ModalInstanceCtrl like this:
function onKeydown(evt) {
if (evt.ctrlKey && evt.which === 13) { // enter key
evt.preventDefault();
$scope.$apply(function () {
$scope.ok();
});
}
}
$document.on('keydown', onKeydown);
$scope.$on('$destroy', function () {
$document.off('keydown', onKeydown);
});
Example Plunker: http://plnkr.co/edit/CR0HxGzCVK2V2dAxzjX4?p=preview
Hope this helps.
Here is a directive working with Angular 1.3. It can be added as attribute of a form or anywhere inside a modal. Only one modal should be opened at at once.
/**
* Submit form in modal on Enter key pressed.
* Usage: <form org-submit-onenter="myModalCtrl.ok()"></form>
*
* Only one modal should be opened at at once.
*/
function orgSubmitOnenter($document) {
return {
restrict: 'A',
priority: 0,
link: function($scope, element, attr) {
if (!attr.orgSubmitOnenter) return;
function onKeydown(event) {
if (event.which === 13) { // Enter key
event.preventDefault();
$scope.$eval(attr.orgSubmitOnenter);
}
}
$document.on('keydown', onKeydown);
$scope.$on('modal.closing', function () {
$document.off('keydown', onKeydown);
});
}
}
}
angular
.module('directives')
.directive('orgSubmitOnenter', orgSubmitOnenter);

AngularJS directive remove class in parent element

I am using the following code to add / remove class "checked" to the radio input parent. It works perfectly when I use JQuery selector inside the directive but fails when I try to use the directive element, can someone please check my code and tell me why it is not working with element and how I can possibly add/ remove class checked to the radio input parent while using element instead of the jquery selectors? Thanks
.directive('disInpDir', function() {
return {
restrict: 'A',
scope: {
inpflag: '='
},
link: function(scope, element, attrs) {
element.bind('click', function(){
//This code will not work
if(element.parent().hasClass("checked")){
scope.$apply(function(){
element.parent().removeClass("checked");
element.parent().addClass("checked");
});
}else{
scope.$apply(function(){
element.parent().addClass("checked");
});
}
//This code works perfectly
$('input:not(:checked)').parent().removeClass("checked");
$('input:checked').parent().addClass("checked");
});
}
};
});
HTML:
<div class="inpwrap" for="image1">
<input type="radio" id="image1" name="radio1" value="" inpflag="imageLoaded" dis-inp-dir/>
</div>
<div class="inpwrap" for="image2">
<input type="radio" id="image2" name="radio1" value="" inpflag="imageLoaded" dis-inp-dir/>
</div>
Your code actually works for me in Plnkr (more or less):
http://plnkr.co/edit/vJJRYQQxH7u2bKSc27UA?p=preview
When you run this, the 'checked' class gets correctly added to the parent DIVs using only the first code you included. (I commented out the jQuery mechanism - I didn't add jQuery to this page, as a test.)
However, I think what you're trying to accomplish isn't working out because you're only capturing click events. The radio button that loses its checked attribute doesn't get a click event, only the next one does. In jQuery your selector is really broad - you're hitting every radio button, so it does what you want. But since you only trap click on the radio button that receives the click, it doesn't do what you want using the other pattern. checked gets added, but never removed.
A more Angular-ish pattern would be something like this:
http://plnkr.co/edit/HN7tLxkRA0jUL5GPjk5V?p=preview
link: function($scope) {
$scope.checked = false;
$scope.$watch('currentValue', function() {
$scope.checked = ($scope.currentValue === $scope.imgNumber);
});
$scope.setValue = function() {
$scope.currentValue = $scope.imgNumber;
};
}
What you see here lets Angular do all the dirty work, which is kind of the point. You can actually go a lot further than this - you could probably cut half the code out and do it all with expressions. The point is that in Angular, you really want to focus on the DATA (the model). You wire all of your behaviors and events up (controller) to things that manipulate that data, and then wire up all your DOM styles, classes, templates (view), etc. up to conditionals against that same data. And that is the point of MVC!

Modifying a directive to 'watch' a ctrl property?

I have a directive which checks if they click submit on a form. If so, it adds the 'submitted' class. This allows me to style the form inputs as red only when they have submitted a form (I hate having it red in real-time as they're typing).
'use strict';
app.directive('formSubmitted', function() {
return {
restrict: 'A',
require: 'form',
link: function(scope, element, attrs, ctrl) {
ctrl.$submitted = false;
element.on('submit', function() {
scope.$apply(function() {
ctrl.$submitted = true;
element.addClass('submitted');
});
});
}
};
});
The problem is 'resetting' a form once it has been submitted successfully... I need to remove the submitted class from inside the controller. I have tried a lot of ways to do this without success... as in this pseudo-code...
angular.element($scope.myForm).removeClass('submitted');
What I am thinking is instead of doing that from the controller (which doesn't work anyway), that I try to make the 'submitted' class mirror the $submitted property on ctrl... This way I could do...
$scope.myForm.$submitted = false and the class would update appropriately.
I have no idea even where to begin with though, and googling isn't helping...
Thanks!
A simple approach I have used in situations like this is leveraging the Angular ngClass directive and binding to a property on the controller that maintains whether the state is submitted or not. Something like so:
<button ng-click="isSubmitted = !isSubmitted">Submit</button>
<form ng-class="{submitted: isSubmitted}">
</form>
You can use the ng-class directive:
<form name="myForm" ng-class="{submitted: $submitted}">
See the doc here: https://docs.angularjs.org/api/ng/directive/ngClass
Within the controller handling the form submission, you certainly have a submit function:
$scope.submit = function (form) {
$scope.$submitted = true;
if (form.$invalid) {
return;
}
// Actually send data to backend, eventually receiving a promise
promiseFormBackend = MyService.sendForm();
promiseFromBackend.then(function () {
$scope.$submitted = false: // resetting the form class
});
}

Validate Form with AngularJS on Enter keypress

I have a description of field in my AngularJS code:
input.form-control(type='password' placeholder='password' id='passwordInput'
name='passwordInput' ng-model='credentials.password
ng-enter='loginByEnter(credentials,loginForm.$valid)'
ng-maxlength='{{passwordMaxLen}}' required form-control)
The line in question is ng-enter one. The function loginByEnter is:
$scope.loginByEnter = function(credentials,htmlValidated) {
if (htmlValidated) {
$scope.login(credentials);
}
};
And custom directive ng-enter is
.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();
}
});
The purpose of all this: I want user to proceed with login when he netered password and hit Enter (so he does not have to press separate Login button elsewhere). But I also need to validate the form. Can it be done more elegantly, without querying a validation function on hitting enter?
You need to take a look at this article.
It explains how you can do form validation in AngularJs.
Simply put, you need to enclose the input tag inside a form tag and then you need to use the ng-submit directive on the form tag to allow the user to submit the form without hitting the login button.
You don't need to make use of your custom ng-enter directive.
"ngEnter" or similar keyboard event processors are not needed for form submission. One can use ngSubmit on <form> elements to handle either key press (Enter) or button click submission.
View
<form ng-submit="onLoginSubmit()">
<input type="text" ng-model="username" />
<input type="password" ng-model="password" />
<input type="submit" />
</form>
Controller
function Controller($scope, ...) {
$scope.onLoginSubmit = function() {
if(validate($scope.username, $scope.password)) {
// Magic
}
}
}

Resources