I know this has been covered many times and most articles refer to this bit of code: Modal window with custom URL in AngularJS
But I just don\'t get it. I don\'t fin
I answered a similar question, and provided an example here:
Modal window with custom URL in AngularJS
Has a complete working HTML and a link to plunker.
It's intuitive to think of a modal as the view component of a state. Take a state definition with a view template, a controller and maybe some resolves. Each of those features also applies to the definition of a modal. Go a step further and link state entry to opening the modal and state exit to closing the modal, and if you can encapsulate all of the plumbing then you have a mechanism that can be used just like a state with ui-sref
or $state.go
for entry and the back button or more modal-specific triggers for exit.
I've studied this fairly extensively, and my approach was to create a modal state provider that could be used analogously to $stateProvider
when configuring a module to define states that were bound to modals. At the time, I was specifically interested in unifying control over modal dismissal through state and modal events which gets more complicated than what you're asking for, so here is a simplified example.
The key is making the modal the responsibility of the state and using hooks that modal provides to keep the state in sync with independent interactions that modal supports through the scope or its UI.
.provider('modalState', function($stateProvider) {
var provider = this;
this.$get = function() {
return provider;
}
this.state = function(stateName, options) {
var modalInstance;
$stateProvider.state(stateName, {
url: options.url,
onEnter: function($modal, $state) {
modalInstance = $modal.open(options);
modalInstance.result['finally'](function() {
modalInstance = null;
if ($state.$current.name === stateName) {
$state.go('^');
}
});
},
onExit: function() {
if (modalInstance) {
modalInstance.close();
}
}
});
};
})
State entry launches the modal. State exit closes it. The modal might close on its own (ex: via backdrop click), so you have to observe that and update the state.
The benefit of this approach is that your app continues to interact mainly with states and state-related concepts. If you later decide to turn the modal into a conventional view or vice-versa, then very little code needs to change.
The $modal itself doesn't have a close() funcftion , I mean If you console.log($modal) , You can see that there is just an open() function.
Closing the modal relies on $modalInstance object , that you can use in your modalController.
So This : $modal.close(result) is not actually a function!
Notice : console.log($modal); ==>> result :
Object { open: a.$get</k.open() }
// see ? just open ! , no close !
There is some way to solve this , one way is :
First you must define a controller in your modal like this :
$modal.open({
templateUrl: 'components/new-item/new-item.html',
controller:"MyModalController"
});
And then , Later on , :
app.controller('MyModalController',function($scope,$modalInstance){
$scope.closeMyModal = function(){
$modalInstance.close(result);
}
// Notice that, This $scope is a seperate scope from your NavbarCtrl,
// If you want to have that scope here you must resolve it
});
Here is a provider
that improves @nathan-williams solution by passing resolve
section down to the controller
:
.provider('modalState', ['$stateProvider', function($stateProvider) {
var provider = this;
this.$get = function() {
return provider;
}
this.state = function(stateName, options) {
var modalInstance;
options.onEnter = onEnter;
options.onExit = onExit;
if (!options.resolve) options.resolve = [];
var resolveKeys = angular.isArray(options.resolve) ? options.resolve : Object.keys(options.resolve);
$stateProvider.state(stateName, omit(options, ['template', 'templateUrl', 'controller', 'controllerAs']));
onEnter.$inject = ['$uibModal', '$state', '$timeout'].concat(resolveKeys);
function onEnter($modal, $state, $timeout) {
options.resolve = {};
for (var i = onEnter.$inject.length - resolveKeys.length; i < onEnter.$inject.length; i++) {
(function(key, val) {
options.resolve[key] = function() { return val }
})(onEnter.$inject[i], arguments[i]);
}
$timeout(function() { // to let populate $stateParams
modalInstance = $modal.open(options);
modalInstance.result.finally(function() {
$timeout(function() { // to let populate $state.$current
if ($state.$current.name === stateName)
$state.go(options.parent || '^');
});
});
});
}
function onExit() {
if (modalInstance)
modalInstance.close();
}
return provider;
}
}]);
function omit(object, forbidenKeys) {
var prunedObject = {};
for (var key in object)
if (forbidenKeys.indexOf(key) === -1)
prunedObject[key] = object[key];
return prunedObject;
}
then use it like that:
.config(['modalStateProvider', function(modalStateProvider) {
modalStateProvider
.state('...', {
url: '...',
templateUrl: '...',
controller: '...',
resolve: {
...
}
})
}]);