Angularjs Bootstrap modal closing call when clicking outside/esc - javascript

I am using the Angular-ui/bootstrap modal in my project.
Here is my modal:
$scope.toggleModal = function () {
$scope.theModal = $modal.open({
animation: true,
templateUrl: 'pages/templates/modal.html',
size: "sm",
scope: $scope
});
}
One is able to close the modal by clicking the ESC button or clicking outside the modal area. Is there a way to run a function when this happens? I am not quite sure how to catch the sort of closing.
I know that I can manually dismiss a modal by having a ng-click="closeModal()" like this:
$scope.closeModal = function () {
$scope.theModal.dismiss('cancel');
};

Yes you can. It causes a dismiss event and the promise is rejected in that case. Also, note that the $modal.open() method returns an object that has a result property that is a promise.
With the promise you can...
//This will run when modal dismisses itself when clicked outside or
//when you explicitly dismiss the modal using .dismiss function.
$scope.theModal.result.catch(function(){
//Do stuff with respect to dismissal
});
//Runs when modal is closed without being dismissed, i.e when you close it
//via $scope.theModal.close(...);
$scope.theModal.result.then(function(datapassedinwhileclosing){
//Do stuff with respect to closure
});
as a shortcut you could write:
$scope.theModal.result.then(doClosureFn, doDismissFn);
See ref
The open method returns a modal instance, an object with the following properties:
close(result) - a method that can be used to close a modal, passing a result
dismiss(reason) - a method that can be used to dismiss a modal, passing a reason
result - a promise that is resolved when a modal is closed and rejected when a modal is dismissed
opened - a promise that is resolved when a modal gets opened after downloading content's template and resolving all variables
'rendered' - a promise that is resolved when a modal is rendered.

Old question, but if you want to add confirmation dialogs on various close actions, add this to your modal instance controller:
$scope.$on('modal.closing', function(event, reason, closed) {
console.log('modal.closing: ' + (closed ? 'close' : 'dismiss') + '(' + reason + ')');
var message = "You are about to leave the edit view. Uncaught reason. Are you sure?";
switch (reason){
// clicked outside
case "backdrop click":
message = "Any changes will be lost, are you sure?";
break;
// cancel button
case "cancel":
message = "Any changes will be lost, are you sure?";
break;
// escape key
case "escape key press":
message = "Any changes will be lost, are you sure?";
break;
}
if (!confirm(message)) {
event.preventDefault();
}
});
I have a close button on the top right of mine, which triggers the "cancel" action. Clicking on the backdrop (if enabled), triggers the cancel action. You can use that to use different messages for various close events. Thought I'd share in case it's helpful for others.

You can use the "result" promise returned by $modal.open() method. As bellow:
$scope.toggleModal = function () {
$scope.theModal = $modal.open({
animation: true,
templateUrl: 'pages/templates/modal.html',
size: "sm",
scope: $scope
});
$scope.theModal.result.then(function(){
console.log("Modal Closed!!!");
}, function(){
console.log("Modal Dismissed!!!");
});
}
Also you can use "finally" callback of "result" promise as below:
$scope.theModal.result.finally(function(){
console.log("Modal Closed!!!");
});

In my case, when clicking off the modal, we wanted to display a prompt warning the user that doing so would discard all unsaved data in the modal form. To do this, set the following options on the modal:
var myModal = $uibModal.open({
controller: 'MyModalController',
controllerAs: 'modal',
templateUrl: 'views/myModal.html',
backdrop: 'static',
keyboard: false,
scope: modalScope,
bindToController: true,
});
This prevents the modal from closing when clicking off:
backdrop: 'static'
And this prevents the modal from closing when hitting 'esc':
keyboard: false
Then in the modal controller, add a custom "cancel" function - in my case a sweet alert pops up asking if the user wishes to close the modal:
modal.cancel = function () {
$timeout(function () {
swal({
title: 'Attention',
text: 'Do you wish to discard this data?',
type: 'warning',
confirmButtonText: 'Yes',
cancelButtonText: 'No',
showCancelButton: true,
}).then(function (confirm) {
if (confirm) {
$uibModalInstance.dismiss('cancel');
}
});
})
};
And lastly, inside the modal controller, add the following event listeners:
var myModal = document.getElementsByClassName('modal');
var myModalDialog = document.getElementsByClassName('modal-dialog');
$timeout(function () {
myModal[0].addEventListener("click", function () {
console.log('clicked')
modal.cancel();
})
myModalDialog[0].addEventListener("click", function (e) {
console.log('dialog clicked')
e.stopPropagation();
})
}, 100);
"myModal" is the element you want to call the modal.cancel() callback function on.
"myModalDialog" is the modal content window - we stop the event propagation for this element so it won't bubble up to "myModal".
This only works for clicking off the modal (in other words clicking the backdrop). Hitting 'esc' will not trigger this callback.

Instead of ng-click="closeModal()" you can try ng-click="$dismiss()"
<button ng-click="$dismiss()">Close</button>

We can call jquery 'On' event as well in the controller like this. here "viewImageModal" is the id of modal popup.
constructor($scope: AuditAppExtension.IActionPlanScope, dataSvc: ActionPlanService, Upload, $timeout, $mdToast: any) {
$('#viewImageModal').on('shown.bs.modal', function (e) {
console.log("shown", e);
$scope.paused = false;
$modal.find('.carousel').carousel('cycle');
});
$('#viewImageModal').on('hide.bs.modal', function (e) {
console.log("hide", e);
return true;
});
}

Related

Angular: Show Material Popup before Closing Browser Window

I am trying to show Angular Material Dialog Box (Popup window), when User hits the Chrome Window Close button (upper right). The Dialog modal should hold prompt the user, if they want to save changes, or cancel,
However it only shows the modal for quick second, then closes without waiting for user.
Using code reference below. How can it be fixed ?
How can we detect when user closes browser?
#HostListener('window:beforeunload', ['$event'])
beforeunloadHandler(event) {
this.openDocumentSaveDialog();
}
public openDocumentSaveDialog(): void {
const documentSaveDialogRef = this.documentSaveDialog.open(DocumentSaveDialogComponent, {
width: '600px',
height: '200px',
disableClose: true,
autoFocus: false,
data: null
});
documentSaveDialogRef.afterClosed().subscribe(result => {
this.closeMenu.emit()
});
}
Note: We do Not want to display Native chrome browser popup, but a custom popup .
Angular Material Dialog Box:
https://material.angular.io/components/dialog
The beforeunload event doesn't support a callback function that returns a promise so you can't show the popup and return value as it isn't a sync operation.
what you can do instead is just returning false always or call
event.preventDefault()
and if the user decided to leave the page you can call
window.close(....)
if not you already have cancelled the event.
so your code should look something like this
#HostListener('window:beforeunload', ['$event'])
beforeunloadHandler(event) {
this.openDocumentSaveDialog();
event.preventDefault();
event.returnValue = '';
return false;
}
public openDocumentSaveDialog(): void {
const documentSaveDialogRef =
this.documentSaveDialog.open(DocumentSaveDialogComponent, {
width: '600px',
height: '200px',
disableClose: true,
autoFocus: false,
data: null
});
documentSaveDialogRef.afterClosed().subscribe(result => {
if(!result)
window.close()
this.closeMenu.emit()
});
}
I am afraid that browser security won't allow you to prevent the user from closing the window. In my opinion this is not possible, you can only show the native window that warns the user about losing the data if closing the browser window.
This works for me. But you have no control over the display!
#HostListener('window:beforeunload', ['$event'])
showAlertMessageWhenClosingTab($event) {
$event.returnValue = 'Your data will be lost!';
}

How to customize close dialog function for ngDialog?

I have to implement a customized close dialog function for close button of ngDialog.
As per requirement in some cases (where there is a form) I have to show another ngDialog confirm popup asking if user really want to close the dialog or not so there are 2 options 'YES' and 'NO' which has this behavior.
I have tried it with preCloseCallback() method but somehow it did not worked for me as it does not wait for user confirmation. It is just like the function called on click of close and dialog closed or stays open depending on what I return from function immediately. If I don't return anything it assumes it to be true and closes the dialog.
Can anybody please let me know the way to solve this issue?
Here comes the nice solutions! It's bit hacky but worked perfectly for my case.
Step 1
Set showClose option false while opening dialog.
// In controller
PopUpFactory.openModal('SOME_NAME','SOME_URL.html', 'ngdialog-theme-default SOME_EXTRA_CSS', $scope, function(value){
console.log("Done:", value);
},'SOME_CONTROLLER',false); // false passes to set **showClose** option false
// In common Factory
function openModal(name, templateUrl, classes, scope, callback, ctrl, showClose){
if(showClose === undefined){
showClose = true;
}
ngDialog.openConfirm({
name: name,
controller: ctrl,
template: templateUrl,
className: classes,
closeByDocument: false, // to prevent popup close by clicking outside
closeByEscape: false, // to prevent popup close by ESC key
closeByNavigation : true, // to close popup on state navigation
scope: scope,
disableAnimation: true,
showClose: showClose,
preCloseCallback: function(value) {
return true;
}
}).then(function (value) {
callback(value);
});
}
Step 2
Write common close button handling function
// In Common Factory
/**
* Customize close for any open modal form
* #param isDirty - flag saying if form is dirty
* #param $scope - scope object of current open form
* #param $event - $event object passed from close button of open form
*/
var closeConfirmOpen = false;
function closeForm(isDirty,$scope,$event){
// following lines are important to prevent default behavior of ngDialog close event
if($event){
$event.preventDefault();
$event.stopPropagation();
}
if(isDirty){
var msg = $filter('translate')('navigateAwayWithoutSavingConfirmMsg');
closeConfirmOpen = true;
confirmPopUp('Warning', msg, null, 'leavePage', 'red', 'stayOnPage', function(isOK){
if(isOK == 1){
$scope.closeThisDialog();
}
closeConfirmOpen = false;
});
}else{
$scope.closeThisDialog();
}
}
Step 3
Write a close function in controller to call factory function
/**
* Close sample location modal
*/
$scope.closeForm = function($event){
PopUpFactory.closeForm($scope.formName.$dirty,$scope,$event);
}
Step 4
Add following line after defining header/title for HTML of ngDialog
<div id="SOME_ID" class="ngdialog-close" ng-click="closeForm($event)"></div>
Yooo... done the job...!!!
The best part of this solutions is a common code for closing any form, so once you done with factory function, you only need to add close button wherever required in HTML and add simple close function in controller

Close dialog onclick close icon in alertify.js

I am useing alertify dialog and I have added a close icon in the dialog as follows
<span class='close-button' id='closebutton'></span>
I am trying to close the dialog onclicking the closing the close icon as follows. But I am facing an error as " cannot call methods on dialog prior to initialization; attempted to call method 'close' closebutton is not defined." and unable to close the dialog. Please let me know if there is any solution. Thanks in advance.
$("#closebutton").on( 'click', function () { $('#submitdialog').dialog('close');
});
You can use the name of your dialog to access the close method:
First create the dialog:
alertify.dialog('myAlert',function(){
return{
main:function(message){
this.message = message;
},
setup:function(){
return {
buttons:[{text: "cool!", key:27/*Esc*/}],
focus: { element:0 }
};
},
prepare:function(){
this.setContent(this.message);
}
}});
Then you can close it in this way:
//close it
alertify.myAlert().close()
Notice that myAlert is the name of the dialog.

Angular modal close function not executing

The loading modal is created correctly, but when the finally block is hit it does not close it. Is there any known reason for this? The loading time is minimal but I still need it for cases where there is a delay. I am testing with a device and in Chrome - The issue only arises when it is being run in Chrome.
$scope.init = function() {
var dialog = Modals.openLoadingModal();
OfflineManager.getTemplates().then(function(templates) {
$scope.templates = templates.map(function(e) {
// get e
return e;
});
OfflineManager.getInspections().then(function(inspections) {
$scope.inspections = inspections.map(function(e) {
// get e
return e;
});
}).finally(function() {
dialog.close();
});
});
};
The modal view:
<div class="loadingModal">
<data-spinner data-ng-init="config={color:'#fff', lines:8}" data-config="config"></spinner>
</div>
The modal service:
this.openLoadingModal = function(callback) {
var opts = {
backdrop: true,
backdropClick: false,
keyboard: false,
templateUrl: 'views/modals/loading.html'
};
return this.open(opts, callback, null);
};
this.open = function(opts, closeHandler, dismissHandler, model) {
opts.resolve = { modalModel:function() { return model; }};
opts.controller = opts.controller || 'ModalController';
$('div, input, textarea, select, button').attr('tabindex', -1);
var modalInstance = $modal.open(opts);
modalInstance.result.then(function(result) {
$('div, input, textarea, select, button').removeAttr('tabindex');
if (closeHandler) {
closeHandler(result);
}
}, function(result) {
$('div, input, textarea, select, button').removeAttr('tabindex');
if (dismissHandler) {
dismissHandler(result);
}
});
return modalInstance;
};
After some searching I found the following solution which waits until the modal has finished opening before executing:
.finally(function() {
dialog.opened.then(function() {
dialog.close();
});
});
Source:
Call function after modal loads angularjs ui bootstrap
Per the ui.bootstrap docs - http://angular-ui.github.io/bootstrap/versioned-docs/0.13.3/#/modal
result - a promise that is resolved when a modal is closed and rejected when a modal is dismissed
It looks like you're trying to use the wrong promise to execute your logic. result gets triggered as a product of calling $modalInstance.close or $modalInstance.dismiss. If you're trying to close your modal programmatically (as opposed to closing via ng-click within the modal template/controller) you need to call $modalInstance.close or $modalInstance.dismiss directly, then your result.then will execute.

timeout-error when testing login-dialog using protractor and angular-strap modal

I have a login-dialog using a angular-strap modal, which gets invoked by:
scope.authModal = $modal({
template: '/components/login/login.html',
show: false,
scope: scope,
backdrop: 'static'
});
(that code is inside the link function of a login-directive.)
Now, my protractor code looks like this:
it('should perform login properly', function () {
browser.manage().deleteAllCookies();
element(by.model('login.username')).sendKeys('xy123');
element(by.model('login.password')).sendKeys('abz89');
element(by.binding("guiText.loginButton")).click();
browser.waitForAngular();
expect(element(by.id('login.username')).isPresent()).to.eventually.equal(false);
});
In another test above the element(by.id('login.username')).isPresent() has been proved to equal true when the login-dialog is visible.
The problem is, I'm getting Error: timeout of 10000ms exceeded with that test. In the browser I can see, that the credentials are typed in correctly and the button is being clicked. The login modal disappeas and then nothing happens and the browser is eventually running in to that timeout exception after waiting 10 seconds.
I had the same problem and I did below to solve this.
Write this function in your helper file and call this to click on login button in your code. Try to access the button by Id and then pass the id in this function, if not id then update the function as per your need
var clickAndWait= function (btnId) {
var returnVal = false;
browser.wait(function () {
if (!returnVal) {
element(by.id(btnId)).click().then(function () {
returnVal = true;
});
}
return returnVal;
}, 30000);
};

Categories

Resources