I open an $mdDialog
$scope.openDialog = function(){
$mdDialog
.show({
// ...
clickOutsideToClose: true,
controller: MyDialogController
})
.then(function(successData) {
console.log("Closed as success: " + successData);
// Closed as success: hidden_from_button
})
.catch(function(errorData) {
console.log("Closed as error: " + errorData);
})
.finally(function(closeData) {
console.log("On every close data: " + closeData);
});
};
function MyDialogController($scope, $mdDialog) {
$scope.hide = function(){
console.log("Close 1");
$mdDialog.hide('hidden_from_button');
}
}
I want to learn how to send parameters to the .finally and .catch functions of the $mdDialog. Specifically when the dialog is closed with the option clickOutsideToClose: true. I can't find a way to do it.
This is the relevant code of my dialog:
<md-dialog>
<md-toolbar>
<md-button ng-click="hide()">
</md-button>
</md-toolbar>
<md-dialog-content>
<!-- Some more code -->
</md-dialog-content>
</md-dialog>
I would like to be able to print the messages:
Closed as success: hidden_from_button (Which I already did)
Closed as error: data_passed_as_error
On every close data: data_passed_every_time
I tried adding to MyDialogController the function:
$scope.on("$destroy", function(ev) {
console.log("Close 2");
ev.preventDefault();
$mdDialog.cancel('data_passed_as_error');
}
But the printed output, from all related console.log of that action is:
Close 2
Closed as error: undefined
On every close data: undefined
Which tells me that the parameter is not being received. Besides that, if I use this fuction, after closing the dialog with ng-click="hide()", the last function is called as well. This is the output of all related console.log of clicking on close button:
Close 1
Close 2
Closed as success: hidden_from_button
On every close data: undefined
I want to handle properly the $mdDialog and be able to send data to the different .finally, .then and .catch functions of the promise it creates, without colliding with each other.
I need to send a special parameter when the user clicks outside the dialog to the parent controller. How do I do that?
Related
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
I have a modal dialog where the user can select files to be uploaded. The actual file select/upload is handled by ng-file-upload. When the user selects one or more file, they are added to a list in the dialog, showing progress, completion and failure statuses for each element. The list of items are handled inside a custom directive, since it's used other places as well.
I need to prevent the user from dismissing the dialog while files are still uploading, and that's a challenge for me, cause the button for closing the dialog is in one controller, while the list of uploads is in another (the directive controller). I have solved that by giving and empty list to the directive like this:
//extract from directive:
var directive = {
...
scope: {
'files': '='
}
}
//extract from usage
<uploadFiles files="files" />
Now the outer controller and the inner controller shares the list of files uploading.
So when the user tries to dismiss the dialog by clicking the Close button, I first check if the list contains files still uploading, and if so, I disable the button and display a spinner and a 'please wait'-text.
//from the outer controller
function onModalOk() {
if (uploadInProgress()) {
waitForCompletionBeforeClosingDialog();
} else {
closeDialog();
}
}
the waitForCompletionBeforeClosingDialog() is implemented by setting up a deep watch on the files array. Each time the watch is triggered, I loop through to see if every file has completed. If so, I delete the watch and dismiss the dialog.
function waitForCompletionBeforeClosingDialog() {
$scope.showWaitText = true;
var unregisterWatchForCompletion = $scope.$watch('files', function(files) {
if (allCompleted(files)) {
unregisterWatchForCompletion();
closeDialog();
}
}, true);
}
Everything is working ok, except for one little thing...
In the console, I get this error:
TypeError: Illegal invocation
at equals (angular.js:931)
at equals (angular.js:916)
at Scope.$digest (angular.js:14302)
at Scope.$apply (angular.js:14571)
at angular.js:16308
at completeOutstandingRequest (angular.js:4924)
at angular.js:5312
and it's fired in a tight loop.
I have tried debugging this error, but with no luck..
Do anyone have any ideas?
Is there better ways of doing this all together?
What about using an $httpInterceptor to keep count of the amount of active requests?
something like:
angular.module('someModule').provider('httpStatus', ['$httpProvider', function ($httpProvider) {
var currentRequestCount = 0;
var interceptor = ['$q', function ($q) {
return {
request: function (config) {
currentRequestCount++;
return config;
},
response: function (response) {
currentRequestCount--;
return response;
},
responseError: function (rejection) {
currentRequestCount--;
return $q.reject(rejection);
}
}
}];
$httpProvider.interceptors.push(interceptor);
this.$get = function () {
return {
isWaiting: function () {
return currentRequestLength > 0;
}
}
};
}]);
You could inject the httpStatus service into your dialog and use it to disable the buttons if there are any active requests. May need to add the requestError handler also.
https://docs.angularjs.org/api/ng/service/$http
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;
});
}
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);
};
What I am trying
On Save or Delete of my form a simple notification is poped-up to user
What I have done
events :{
'#save-button click' : 'onSaveBUttonClick',
'#delete-button click' : 'onDeleteButtonClick'
};
onDeleteButtonClick = function(){
//popup appears to confirm delete
this.model.on('sync',function(model){
model.off('sync');
alert("project deleted");
},this);
this.model.destroy();
}
onSaveBUttonClick = function(){
//popup appears to confirm delete
this.model.on('sync',function(){
model.off('sync');
alert("project Saved");
},this);
this.model.save();
}
The problem
I click on the delete button and say , select , cancel. Here the model.on('sync') is bound to the model.
Now when I click save , and confirm , the model.on('sync') is called twice (one bound by delete button and one bound by save button).
So I am getting 2 pop-ups Project deleted first and project saved after it.
How can I avoid this?
You can use the success options in model.save and model.destroy
destroy model.destroy([options])
Destroys the model on the server by delegating an HTTP DELETE request to Backbone.sync. Returns
a jqXHR object, or false if the model isNew. Accepts success and error
callbacks in the options hash.
save model.save([attributes], [options])
[...]
save accepts success and error callbacks in the options hash.
Your methods could look like
onDeleteButtonClick = function(){
this.model.destroy({
success: function() {
alert("project deleted");
}
});
}
onSaveBUttonClick = function(){
this.model.save(null, {
success: function() {
alert("project saved");
}
});
}