How can I submit an angular-ui modal with keyboard - angularjs

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);

Related

Retrofitting "alert()" on delete buttons

I have an angular application which contains several delete buttons somewhat like this:
<button class="btn btn-default" ng-click="delete($index)">x</button>
Now as we are nearing production deployment and I want delete buttons to behave nicely and "alert()" the user before actually deleting the object.
How can I retro-fit this feature in all the delete buttons through a directive. I would like to have a directive called "ask":
<button ask class="btn btn-default btn-xs" ng-click="delete($index)">x</button>
which I can use to affect the behaviour of any button.
Somehow I am not able to think through this
app.directive("ask", function() {
return function(scope, elems, attrs) {
// what to do here:
// 1. shall I change the elems.attr("ng-click")
// 2. or scope.attrs("ngClick")
????
}
});
Please guide me with the best practice and some code samples. Also note that the ng-click callback of all the delete buttons are different and the application already makes extensive use of isolate scope and child scope directives.
Try this implementation
angular
.module('test', [])
.directive('ask', function() {
return {
restrict: 'A',
scope: {ask: '#', onAsk: '&'},
link: function(scope, elem, attrs) {
elem.on('click', function(e) {
if(confirm(attrs.ask)) {
scope.onAsk();
scope.$apply();
}
});
}
};
})
.controller('ItemsCtrl', function($scope) {
$scope.items = [1,2,3,4,5];
$scope["delete"] = function(index) {
$scope.items.splice(index, 1);
};
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="test" ng-controller="ItemsCtrl">
<ul><li ng-repeat="item in items track by $index">{{item}}
<button ask="Are you sure you want to delete item?" on-ask="delete($index)">x</button>
</li>
</ul>
</div>
You can build a directive for this and in template of directive have button element and the ng-click event.
Pass index and delete function as inputs to this directive.
app.directive("deleteFruit", function(){
return{
// You can also pass one more binding if you want to have a specific alert message
scope:{
index: '#', delete:'&'
},
template:'<button class="btn btn-default" ng-click="deleteFruit()">x</button>',
link: function(scope){
scope.deleteFruit = function(){
// When user clicks, first call the alert function then use the promise returned by dialog and check whether user wants to delete or not
//modal.dialog().then(function(userSelectedOption){
// If user wants to delete then go a head and call delete function on main controller
// if(userSelectedOption){
// scope.delete(scope.index);
// }
//})
scope.delete(scope.index);
}
}
}
})
MainCtrl HTML
<delete-fruit delete="delete(index)" index={{$index}}></delete-fruit>
Plunker.

Custom directive to detect out side the button click

I need to write a custom directive to close (or hide) the button when click out side the button (empty area of the DOM). In other words anywhere except within the button ? This behaviour should be applied only for this button when I apply the custom directive.Any guide would be highly appreciated.
<button class="btn btn-primary" ng-click="CreateUpdate()">Submit</button>
app.directive('MyDirective', function() {
//content
});
Within the button directive you can inject $document. Then wire up on click on the document.
<button class="myButton btn btn-primary" ng--lick="CreateUpdate()">Submit</button>
mainModule.directive("myButton", ['$document',
function ($document) {
return {
restrict: "E",
link: function (scope, element, attrs) {
$document.bind('click', function(event){
//Get the element clicked
var clickedElement = angular.element(event.target);
// If the clickedElement is not same as button (say, the clicked element class is different from button class) then close the button.
}
}
})

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 remove :active from bootstrap button

I have a website, where I have multiple buttons. Once a button is pressed I populate a list, though my problem is that the last pressed button keeps to be looking pressed (has the :active class). I thought about using angular's $timeout to reset the button, though the removeClass function doesn't do the trick.
My view looks like this:
div(ng-controller='productButtonController')
div(ng-repeat='product in products')
div.col-md-4
button.btn.btn-block.sell-button(id='{{product._id}}' ng-click='sell()'){{product.name}}
and my controller:
app.controller('productButtonController', ['$scope', '$timeout', 'productServices', 'flash',
function($scope, $timeout, productServices, flash) {
productServices.getProducts()
.success(function(result) {
$scope.products = result.data
})
.error(showErrorMessage(flash))
$scope.sell = function() {
console.log(this.product)
that = this
$('#' + that.product._id).removeClass('active')
}
}
])
Add the angular $window service to your dependencies for the
controller
Call the blur method on the document's active element, which will be
your button.
$window.document.activeElement.blur();
See How do you clear the focus in javascript?.
This code from Justin Poehnelt's handy GIST solves this elegantly.
app.directive('blur', [function () {
return {
restrict: 'A',
link: function (scope, element) {
element.on('click', function () {
element.blur();
});
}
};
}]);
Add the blur attribute to a button/element you need blurred after click. Eg.
<button type="button" blur>Click me</button>
If you simply want to override the focus status of the bootstrap buttons you could do it with:
.btn:focus{
outline: none;
}
Then your buttons should look like:
<button class="btn btn-default">My button 1</button>
It's also important that the stylesheet which overrides the button status is loaded after the bootstrap stylesheet.
EDIT:
Sorry, but the previous step only removes the outline. The background-color of the button still remains the same.
Since bootstrap doesn't append any active classes to the clicked element as far as i know you need to change the :focus status of the button:
$('#' + that.product._id).blur();
Let me know if this works for you.

Prevent form submission on enter key

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

Resources