I am using the Angular UI bootstrap modal dialog and create it within a service:
myApp.factory('ModalService', ['$modal', function($modal) {
return {
trigger: function(template) {
$modal.open({
templateUrl: template,
size: 'lg',
controller: function($scope, $modalInstance) {
$scope.ok = function() {
$modalInstance.close($scope.selected.item);
};
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
}
});
},
close: function() {
// this should close all modal instances
}
};
}]);
How can I close all modal instances when calling ModalService.close() from a controller or whatsoever?
Inject the $modalStack service and call the function $modalStack.dismissAll(), see the code on GitHub for details:
myApp.factory('ModalService', ['$modal', '$modalStack' function($modal, $modalStack) {
return {
trigger: function(template) {
$modal.open({
templateUrl: template,
size: 'lg',
controller: function($scope, $modalInstance) {
$scope.ok = function() {
$modalInstance.close($scope.selected.item);
};
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
}
});
},
close: function(reason) {
$modalStack.dismissAll(reason);
}
};
}]);
I added the below line to prevent browser back button routing and closing the popup. We need to inject $modalStack into angular controller.
event.preventDefault();
$modalStack.dismissAll('close');
This is how i got it working in my project without using any factory or additional code.
//hide any open $mdDialog modals
angular.element('.modal-dialog').hide();
//hide any open bootstrap modals
angular.element('.inmodal').hide();
//hide any sweet alert modals
angular.element('.sweet-alert').hide();
I have a timeout function that emits logout as $rootScope.$emit('logout'); and the listener in my service is as follows:
$rootScope.$on('logout', function () {
//hide any open $mdDialog modals
angular.element('.modal-dialog').hide();
//hide any open bootstrap modals
angular.element('.inmodal').hide();
//hide any sweet alert modals
angular.element('.sweet-alert').hide();
//do something else here
});
I don't know if this is the right approach , but it works for me.
Related
My app uses Angular UI to deal with modals and I think I found a problem, as below:
I have a controller that calls the modal controller:
angular.module('MyApp').controller('MainCtrl', function ($scope, $modal) {
$scope.openModal = function () {
var myModal = $modal.open({
animation: true,
templateUrl: 'ModalContent.html',
controller: ModalCtrl
});
};
});
I also have the modal controller in another file, as a simple function:
function ModalCtrl ($scope, $modalInstance) {
$scope.ok = function () {
$modalInstance.close();
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
};
It's working well, but I think the modal controller have to be inside AngularJs module. The questions are:
Can I do it and keep the modal controller in a separated file?
It's a good practice keep the modal controller outside AngularJs module?
What is the best practice to reuse a modal controller in many pages?
Thanks a lot!
That object you're passing into $modal.open() can be registered as a shared service and the modal controller can be embedded right inside it.
angular.module('myApp').factory('myModalConfig', function() {
return {
animation: true,
templateUrl: 'ModalContent.html',
controller: function($scope, $modalInstance) {
$scope.ok = function () {
$modalInstance.close();
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
}
};
});
Then just inject that along with the $modal service and pass it.
angular.module('MyApp').controller('MainCtrl', function ($scope, $modal, myModalConfig) {
$scope.openModal = function () {
var myModal = $modal.open(myModalConfig);
};
});
I go further than this myself and wrap the whole thing into a custom modal service factory, so I can just inject one thing and open modals like this: profileModals.editProfile(). But that's beyond the scope of this answer.
I am using angular-modal-service library. My logic is : when the modal is open it runs a function from SomeService, and $rootScope.$broadcast from SomeService to modal controller that way I can send resource from service to my modal controller. However, it doesn't fire. Please help me to figure out what I have missed. Thank you.
**Service: **
angular.module('ng-laravel').service('SomeService', function($rootScope, Restangular, CacheFactory, $http) {
this.testFunction = function() {
console.log("from service");
$rootScope.$broadcast('event', {success:'success'});
};
}
**Controller: **
$scope.show = function(customer_id) {
ModalService.showModal({
templateUrl: 'modal.html',
inputs: {
customer_id: customer_id
},
scope: $scope,
controller: function($scope, close) {
$scope.customer_id = customer_id;
$scope.close = function(result) {
close(result, 500); // close, but give 500ms for bootstrap to animate
};
$scope.$on('event', function(event, data){
alert('yes');
console.log('from modal controller');
});
}
}).then(function(modal) {
SomeService.testFunction(customer_id, tour_id);
modal.element.modal();
modal.close.then(function(result) {
$scope.message = "You said " + result;
});
});
};
After switching the function it works, but...
how could i pass data in to modal? like ui-bs-modal, they have resolve.
You're being broadcasting event before events from modal controller are binding. So before broadcasting event make sure that event listeners are registered(meaning modal controller has been loaded). So call SomeService.testFunction(); after showModal method.
$scope.show = function(customer_id) {
ModalService.showModal({
templateUrl: 'modal.html',
inputs: {
customer_id: customer_id
},
scope: $scope,
controller: function($scope, close) {
//code as is
//listeners will get register from here.
}
})
.then(function(modal) {
SomeService.testFunction(); //broadcasting event
}).catch(function(error) {
// error contains a detailed error message.
console.log(error);
});
};
You are broadcasting the event, before the modal controller is instantiated or created, as service function is called before ModalService.showModal. Try changing the order. That should work fine.
Inside $scope.show try this order
$scope.show = function(){
ModalService.showModal({
....
// Listen for broadcast event
});
SomeService.testFunction();
}
I wan create function to close modal . I try some thing like this .
Code for open the modal
$scope.showPreloader = function() {
$scope.$modalInstance = $modal.open({
animation: false,
templateUrl: preloaderTemplate,
size: 'sm',
windowClass: 'preloader',
})
};
To hide modal
$scope.hidePreloader = function() {
$scope.$modalInstance.dismiss('cancel');
};
The modal only open and not close it . I run them like this .
$scope.simulateLoader = function(message) {
var runIns = $scope.showPreloader();
setTimeout(function () {
$scope.hidePreloader();
}, 2000);
}
The close function didn't work.
Edit: I think the close function is run . It only not remove the modal . I set backdrop to false . Backdrop cant be click during that time out .
Use Angular $timeout instead of window.setTimeout, because $timeout will call $scope.$apply to trigger $digest and update the view:
$timeout(function () {
$scope.hidePreloader();
}, 2000);
Remember to inject $timeout when declaring the controller, say:
app.controller('MyCtrl', ['$scope', '$timeout', function ($scope, $timeout)...
Try like this :
$scope.showPreloader = function() {
$scope.$modalInstance = $modal.open({
animation: false,
templateUrl: preloaderTemplate,
size: 'sm',
windowClass: 'preloader',
scope: $scope,
controller: ['$scope','$modalInstance', function ($scope, $modalInstance) {
$scope.hidePreloader = function () {
$modalInstance.dismiss('cancel');
};
}
})
};
and then call your $scope.hidePreloader method to dismiss modal popup
I'm using a solution found here: How to close Angular UI Modal from anywhere
to close a UI bootstrap modal that I'm using as an HTTP interceptor to show loading.
However, this solution closes ALL open modals since $modalStack.dismissAll() is being used. How do I remove only the modal that's open with the loading. Here's my code:
app.factory('loadingModal', ['$modal', '$modalStack', function($modal, $modalStack) {
return {
trigger: function(template) {
$modal.open({
windowClass: 'modal fade loading-modal',
templateUrl: '/assets/partials/modals/loadingModal.html',
backdrop: 'static',
controller: function($scope, $modalInstance) {
$scope.ok = function() {
$modalInstance.close($scope.selected.item);
};
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
}
});
},
close: function(reason) {
$modalStack.dismissAll(reason);
// $modal.close();
}
};
}])
Not totally clear how you are using this in interceptor but you should be able to return the instance from trigger and then use instance.dismiss()
app.factory('loadingModal'.....
return {
trigger: function(template) {
// return this instance
return $modal.open({.....
......
}
Then store instance wherever you intialize trigger() in interceptor
var modal = loadingModal.trigger('tmpl.html');
Then to close
modal.dismiss();
Of course you could store this instance in service and create a method to close it with a service method also
I'm invoking a bootstrap modal dialog through a link.
I want to start a timer in the angular controller when the dialog pops up. How do I detect the dialog open event in the angular controller to start the timer?
If I start timer in the scope like this,
app.controller('myctrl',
['$scope', '$window', '$timeout', 'svc',
function ($scope, $window, $timeout, svc) {
$scope.countdown = 10;
$scope.runCounter = function () {
$scope.countdown -= 1;
if ($scope.countdown > 0)
$timeout($scope.runCounter, 60000);
}
$scope.runCounter();
}]);
the timer starts when the application starts. I want the timer to start only when the dialog opens.
Thanks.
Check this out.
var modalInstance = $modal.open({...});
modalInstance.opened.then(function() {
alert("OPENED!");
});
The $modal.open() returns an object that, among other properties contains the opened promise, to be used as above.
I assume that you are using modals from http://angular-ui.github.io/bootstrap/.
If you look closely you will see that the component expose a promise that will be resolved when the dialog is opened. Which is what you will need to use. You can do something like that in the controller where the modal is created:
$scope.runCounter = function () {
$scope.countdown -= 1;
if ($scope.countdown > 0)
$timeout($scope.runCounter, 60000);
}
//Creating the dialog
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: ModalInstanceCtrl
}
});
//Add a function for when the dialog is opened
modalInstance.opened.then(function () {
$scope.runCounter
});
See working plunker here
var modalInstance = $modal.open({
templateUrl: '../augustine_app/templates/program_purchase_popup.html',
backdrop: 'static',
controller: function ($scope, $modalInstance) {
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
}
});
if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) {
modalInstance.opened.then(function () {
var modal;
var getModalInterval = function () {
modal = document.getElementsByClassName('modal')[0];
if (modal) {
clearInterval(getModal);
modal.style.marginTop = window.screenTop + 'px';
modal.style.height = 'auto';
modal.style.position = 'absolute';
modal.style.overflow = 'visible';
}
};
modal = document.getElementsByClassName('modal')[0];
if (!modal) {
var getModal = setInterval(getModalInterval, 2000);
}
});
}
};
Unfortunatly the open.then(func) runs before the freaking modal is actually in the DOM. Hence the setInterval.
here is some non jQuery angular code.
For my case, I need to be able to detect when modal is opened inside the modal controller itself.
At first opened promise was resolved even though the modal hasn't been loaded in the DOM yet. By wrapping the call inside a $timeout, opened promise is now resolved after the modal is loaded to the DOM.
$modal.open({
templateUrl: 'modalTemplate.html',
controller: 'modalCtrl'
});
// inside modalCtrl
angular.controller('modalCtrl', ['$modalInstance', '$timeout', function($modalInstance, $timeout) {
$timeout(function() {
$modalInstance.opened.then(function() {
//do work
});
});
}]);