I am using angular material.
I created a factory to display a loading modal.
I can display it, but I can't dismiss it.
Could you tell me why, or tell me how to do it ?
Here is my factory :
(function() {
'use strict';
angular
.module('loveProjectApp')
.factory('ProgressFactory', ProgressFactory);
ProgressFactory.$inject = ['$mdDialog'];
/* #ngInject */
function ProgressFactory($mdDialog) {
var service = {
show: show,
cancel: cancel
};
return service;
////////////////
function show() {
$mdDialog.show({
templateUrl: 'miscellaneous/progress/progress.dialog.html',
parent: angular.element(document.body),
clickOutsideToClose: false,
fullscreen: false
});
}
function cancel() {
$mdDialog.hide();
}
}
})();
And I call it like this :
function logout() {
ProgressFactory.show();
authFactory.logout().then(function() {
ToastFactory.successToast('SUCCESSES.DISCONNECTED');
$state.reload();
ProgressFactory.cancel();
});
So the loader is shown, but isn't dismissed.
I have created a CodePen example of your code and the dialog does close. I have used a $timeout to replicate your authFactory.logout() call.
This makes me think that
The .then callback function is not being called, or
Something is happening before ProgressFactory.cancel(); is called that prevents it from being executed
JS
.controller('AppCtrl', function($scope, $timeout, ProgressFactory) {
$scope.logout = function () {
ProgressFactory.show();
$timeout(function () {
ProgressFactory.cancel();
}, 2000);
}
})
FOUND IT !
My server is running on the same machine that my application uses.
I was calling $mdDialog.cancel(); too fast visibly, so I added a 1s timeout in the factory, and now the dialog dismisses correctly.
It now works like a charm, thank you !
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();
}
Bear with me - I'm an AngularJS newb.
I have a form that I want to be available from anywhere in my app, and I'm trying to figure out how to code that. My current attempt is to put the modal into a service, like this:
.service('NewObjectService', function() {
var svc = this;
svc.showModal = function() {
$ionicModal.fromTemplateUrl('template.html', {
scope: null, // what should I do here?
animation: 'slide-in-up'
}).then(function(modal) {
svc.modal = modal;
modal.show();
});
}
})
.controller('NewObjectController', function() {
$scope.$on('$ionicView.enter', function() {
console.log('NewObjectController');
// setup new object
})
})
Then from anywhere in my app, I can call NewObjectService.showModal() and the modal pops up. That part is working.
The trouble I'm running into is that I can't get my controller to fire, so the initialization never gets called and my new object is null.
It seems like I should actually be calling the modal from within NewObjectController to setup scope, but I tried that and I couldn't figure out how to call that controller from within other controllers - hence the service.
I know I'm just doing something fundamentally wrong, but I'm not sure what it is. Any help?
Update: I also just tried calling one controller from another using a root scope broadcast:
.controller('MainCtrl', function() {
this.showModal = function() {
$rootScope.$broadcast('new_object:show_modal');
}
})
.controller('NewObjectCtrl', function() {
$rootScope.$on('new_object:show_modal', function() {
// show modal
})
})
The problem I'm running into there is that NewObjectCtrl hasn't been invoked at the time MainCtrl runs, so it doesn't catch the broadcast event.
When you declare a service you need to return itself in the Angular declaration ie var svc = {}; return svc; Call svc.showModal from any controller after you've injected the service and pass in the scope. Call the controller from another controller by using $rootScope.$on (receiver) and $rootScope.$emit (from)
.service('NewObjectService', function($ionicModal) {
var svc = {};
svc.showModal = function(_scope) {
$ionicModal.fromTemplateUrl('template.html', {
scope: _scope, // passing in scope from controller
animation: 'slide-in-up'
}).then(function(modal) {
svc.modal = modal;
modal.show();
});
}
return svc;
})
.controller('NewObjectController', function($scope, $rootScope, NewObjectService) {
// fires off when $rootScope.$emit('ShowModal') is called anywhere
$rootScope.$on('ShowModal', function(data) {
NewObjectService.showModal(data._scope);
});
})
.controller('OtherController', function($scope, $rootScope) {
$scope.contactOtherController = function() {
// contact the other controller from this controller
$rootScope.$emit("ShowModal", {scope: $scope});
}
})
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 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.