I'm using Bootstrap's $uibModal to make forms in my web-application. Everything works fine, except that I cannot close the dialog after it's been shown.
I spent two days trying not to post here such a simple question, especially when there was a lot of answers for the same error that I'm receiving, i.e.
"Unknown provider: $uibModalInstanceProvider <- $uibModalInstance <-
ModalInstanceCtrl".
To have some ease in operating with huge application, I divided my code into separate files, also I use 'controller as' style in htmls, so, there's no $scopes in the code.
No matter what I do, I keep receiving this error that I mentioned before.
What I need, is to close the dialog after user have successfully logged in.
index.html:
...
<script src="rf/angular.min.js"></script>
<script src="rf/angular-animate.js"></script>
<script src="rf/angular-touch.js"></script>
<script src="rf/ui-bootstrap-tpls.js"></script>
<!-- Project files -->
<script src="application.js" type="text/javascript"></script>
<script src="frmLogin.js"></script>
</head>
<body>
<!-- Load things into this division -->
<div ng-include src="loaderCtrl.cFragment" ng-app="dgis" ng-controller="applicationController as loaderCtrl"></div>
</body>
application.js
var myApp = angular
.module('dgis', ['ui.bootstrap'])
.controller("applicationController", ['$http', '_gl', '$uibModal', function ($http, _gl, $uibModal) {
_gl.AppReference = this;
// Enable animations
this.animationsEnabled = true;
// Declare modal window
this.showFrmLogin = function (size) {
var modalInstance = $uibModal.open({
animation: this.animationsEnabled,
templateUrl: 'frmLogin.html',
controller: 'ModalInstanceCtrl',
size: size,
backdrop: 'static',
keyboard: false,
resolve: {
items: function () {
return this.items;
}
}
});
};
this.showFrmLogin();
frmLogin.js
myApp.controller('ModalInstanceCtrl', ['_gl', '$uibModalInstance', function (_gl, $uibModalInstance) {
this.login = function () {
angular.forEach(some_array, function (element) {
if (something == element)
// This code executes successfully and shows the page
_gl.AppReference.cFragment = "frmMain.html";
// This line does not close the dialog
$uibModalInstance.close('a');
}
});
}
}]);
And _gl - is a service that I use to hold global variables
use $rootScope
when initializing your modal use $rootScope.modalInstance
You can access it anywhere from the application then.
Hope this helps
Remove the $uibModalInstance references
Instead of "var modalInstance = $uibModal.open({"
Use "$rootScope.modalInstance = $uibModal.open({
Instead of $uibModalInstance.close('a');"
Use "$rootScope.modalInstance.close('a');"
Related
I am trying to load a template file in an AngularStrap popover, however I am having trouble using $templateCache. I seem to be a step further back than the other SO questions, hence this seemingly double one.
Following the API docs I added a <script type="text/ng-template" id="popoverTemplate.html"></script> right before the closing </body> tag. When I use <div ng-include="'popoverTemplate.html'"></div> on my page, I get nothing. If I try using console.log($templateCache.get("popoverTemplate.html")) I get "$templateCache is not defined", which leads me to assume I am missing a crucial step. However, I can't find how to do it in the docs or other SO questions.
EDIT:
Injecting the service was the missing link. However, when I inject the service, the controller's other function no longer works, but if you inject al the function's parameters the working code becomes:
(function() {
"use strict";
angular.module("app").controller("managerController", ["$scope", "imageHierarchyRepository", "$templateCache", function ($scope, imageHierarchyRepository, $templateCache) {
imageHierarchyRepository.query(function(data) {
$scope.hierarchies = data;
});
var template = $templateCache.get("popoverTemplate.html");
console.log(template);
}]);
})();
To use the template script tag . You have to insert it inside the angular application. That is inside the element with the ng-app attribute or the element used to bootstrap the app if you don't use the ng-app tag.
<body ng-app="myapp">
<div ng-template="'myTemplate.html'"></div>
<script type="text/ng-template" id="myTemplate.html">
// whate ever
</script>
</body>
If you want to retrieve the template on a component of the application then you need to inject the service where you want to consume it:
controller('FooCtrl', ['$templateCache', function ($templateCache) {
var template = $templateCache.get('myTemplate.html');
}]);
Or
controller('FooCtlr', FooCtrl);
FooCtrl ($templateCache) {};
FooCtrl.$inject = ['$templateCache'];
EDIT
Do not register two controllers with the same name because then you override the first one with the last one.
(function() {
"use strict";
angular.module("app").controller("managerController",["$scope", "imageHierarchyRepository", "$templateCache", function ($scope, imageHierarchyRepository, $templateCache) {
var template = $templateCache.get("popoverTemplate.html");
console.log(template);
imageHierarchyRepository.query(function(data) {
$scope.hierarchies = data;
});
}]);
})();
Small addition: Although there are few ways to achieve your goals, like wrapping your whole HTML in <script> tags and all that, the best approach for me was to add the $templateCache logic into each Angular directive. This way, I could avoid using any external packages like grunt angular-templates (which is excellent but overkill for my app).
angular.module('MyApp')
.directive('MyDirective', ['$templateCache', function($templateCache) {
return {
restrict: 'E',
template: $templateCache.get('MyTemplate').data,
controller: 'MyController',
controllerAs: 'MyController'
};
}]).run(function($templateCache, $http) {
$http.get('templates/MyTemplate.html').then(function(response) {
$templateCache.put('MyTemplate', response);
})
});
Hope this helps!
This is a bit strange. When I search this issue online I see many pages of Google results and SO solutions... but none seem to work!
In a nutshell, I am trying to implement AngularUI Bootstrap Modal. I keep getting the following error:
Error: [$injector:unpr] Unknown provider: $uibModalInstanceProvider <- $uibModalInstance <- addEntryCtrl
Here is my HTML:
<nav class="navbar navbar-default">
<div class="container">
<span class="nav-col" ng-controller="navCtrl" style="text-align:right">
<a class="btn pill" ng-click="open()" aria-hidden="true">Add New</a>
</span>
</div>
</nav>
Here is my controller:
var app = angular.module('nav', ['ui.bootstrap']);
app.controller('navCtrl', ['$scope', '$uibModal', function($scope, $uibModal) {
$scope.open = function() {
var uibModalInstance = $uibModal.open({
animation: true,
templateUrl: 'addEntry/addEntry.html',
controller: 'addEntryCtrl',
});
};
}]);
And finally, here is my modal code:
var app = angular.module('addEntry', ['firebase', 'ui.bootstrap']);
app.controller('addEntryCtrl', ['$scope', '$firebaseObject', '$state', '$uibModalInstance', function($scope, $firebaseObject, $state, $uibModalInstance) {
$scope.cancel = function() {
$uibModalInstance.dismiss('cancel');
};
$uibModalInstance.close();
}]);
Solutions I've tried:
updated both Angular Bootstrap (Version: 0.14.3) and Angular (v1.4.8)
changed uibModalInstance to modalInstance
changed $uibModalInstance to modalInstance
put my addEntryCtrl inside my ModalInstance
Any thoughts? This has been driving me up the wall for almost 2 days now.
* EDIT *
I should note two things:
1) when I remove $uibModalInstance as a dependency from addEntry, my HTML form submit buttons work just fine and the form looks perfect. Even the redirect occurs correctly (upon submission). The problem remains: the modal still stays on the screen and an error is thrown that $uibModalInstance is undefined. This makes sense since I removed it as a dependency but I obviously still need the modal is close upon submission.
2) Also, I have almost identical code working in another part of my app. The only difference there is that it's working via a factory. Otherwise, the code is identical. Thus, I am confident my dependencies are all there and versions are correct. So. Freaking. Strange.
Thanks!
Answer Found! After hacking away with my friend, we discovered the answer. I wanted to post it here just in case someone else reads this.
It turns out that we had an ng-controller in our modal window that was in a div tag that wrapped the entire html form that was in the modal. Previously, this worked fine when our form was NOT in a modal (it had a separate URL) but for some reason it breaks when it is in a modal. The ng-controller was referencing the addEntryCtrl. Immediately after removing it, the form worked!
The problem was that you were specifying a (or double) controller(s) in 2 places- when opening a modal and inside a template - this is not needed. Remove ng-controller from a template and things will work as expected.Trust me,it will work.
It turns out that if you specify the controller inside the html template (with ng-controller="...") it will not resolve the $uibModalInstance. Specifying the controller from the call to $uibModal.open({controller="...", ...}) will allow it to resolve correctly.
Since you only need the dismiss() and close() methods, you can get them from $scope (named $dismiss and $close) instead, since that will resolve correctly in both ways of instantiating the controller.
var app = angular.module('addEntry', ['ui.bootstrap']);
app.controller('addEntryCtrl', ['$scope', function($scope) {
$scope.cancel = function() {
$scope.$dismiss('cancel');
};
$scope.$close();
}]);
You are trying to reference a controller that is part of a separate module. In order for this to work, you need to inject your secondary module (addEntry) into your main module (nav):
var app = angular.module('nav', ['ui.bootstrap', 'addEntry']);
As you use $uibModal.open() (see lower) and specify explicitly the controller name, you shouldn't put the directive ng-controller in the template.
That cause the error. No ng-controller in the View !
var uibModalInstance = $uibModal.open({
animation: true,
templateUrl: 'addEntry/addEntry.html',
controller: 'addEntryCtrl',
});
Why is $uibModalInstance unable to be injected here?
http://plnkr.co/edit/mi9Ytv0HaqE47ENod4Gn?p=preview
baseController.$inject = ['$uibModal', '$uibModalInstance'];
function baseController($uibModal, $uibModalInstance) {
self.ok = function () {
$uibModalInstance.close(self.selected.item);
};
self.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
I am trying to access $uibModalInstance and if I try to inject it, I get an injection error.
If I don't inject it, then it's Undefined...
So LOTS of great statements here, but none of them quite solved the issue.
amg-argh got my plkr working with the way I had set up my injection
http://plnkr.co/edit/GXXmUosUEnEO3Tk0gUyp?p=preview
The biggest magic came from this edit here...
var childController = function ($scope, $uibModalInstance) {
$scope.ok = function () {
$uibModalInstance.close({ my: 'data' });
}
$scope.cancel = function () {
$uibModalInstance.dismiss();
}
}
self.open = function (size) {
var modalInstance = $uibModal.open({
animation: self.animationsEnabled,
templateUrl: 'help.html',
controller: childController,
size: size
});
};
+++++++++++++++++++++++++++++
I also want to make a note that this SHOULD be done later on as a Service, just as icfantv has pointed out. But I need to figure out the syntax for returning and consuming the modal's promise...
Thank you everyone for your help!!
Cheers!
I suspect the reason your code is failing is because you're trying to inject the modal instance into the controller whose responsibility it is to create the modal. It can't inject it because it doesn't exist yet. Try using separate controllers: one for opening the modal and another for processing the modal contents. You can see an example of this in the Javascript portion of our modal example.
TBH, I've never seen dependencies injected that way before. Is there some problem you're solving by doing that? Any reason you're not just using:
angular.module(...).controller('MyController', ['dep1', dep2', ..., MyControllerFunction]);
function MyControllerFunction(dep1, dep2) {...}
There were a few things wrong with your plunk example:
The order in which you load your scripts is important, you had:
<script data-require="angular.js#1.4.x" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.js" data-semver="1.4.7"></script>
<script src="app.module.js"></script>
<script src="childController.js"></script>
<script src="baseController.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular-animate.js"></script>
<script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.14.3.js"></script>
This will not work as app.module.js depends on the angular-ui-bootstrap.js and childController.js depends on angular-animate.js and childController.js depends on baseController.js You need to load the scripts in the following order:
<script data-require="angular.js#1.4.x" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.js" data-semver="1.4.7"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular-animate.js"></script>
<script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.14.3.js"></script>
<script src="app.module.js"></script>
<script src="baseController.js"></script>
<script src="childController.js"></script>
In childcontroller.js, you have:
angular
.module('app', ['ngAnimate'])
Which is recreating and overwriting the 'app' created in the app.module.js. You should inject the ngAnimate module in your app.module.js like so:
angular
.module('app', ['ngAnimate', 'ui.bootstrap']);
You don't need a reference to $uibModalInstance in your base controller, the:
var modalInstance = $uibModal.open({
animation: self.animationsEnabled,
templateUrl: 'help.html',
controller: 'BaseController',
size: size
});
gives you access to the modal you opened through 'var modalInstance' which has close and dismiss methods.
Imagine I have a singleton user-interface element that I want to provide services to the entire application.
The following "works", in that setMessage() is available anywhere in the application, via $rootScope:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.17/angular.js"></script>
<body ng-app="myApp">
<script>
angular.module('myApp', [])
.directive('myDirective', function($rootScope) {
return {
scope : {},
template: '<div>Status: {{contents}}</div>',
controller: function($scope) {
$rootScope.setMessage = function(contents) {
$scope.contents = contents;
};
}
};
});
</script>
<div my-directive=""></div>
<button ng-click="setMessage('clicked')">Click Me</button>
</body> </html>
It works, but I don't like it. It just doesn't feel anglish to me. I have been toying with the idea of create a service that exports a function to set the value and another function to get it, but that doesn't smell much better.
Any suggestions?
I am trying to use the Angular Bootstrap Modal directive (http://angular-ui.github.io/bootstrap/) as follows, in my controller to open the modal:
function customerSearch() {
var modalInstance = $modal.open({
templateUrl: 'app/customer/customers.modal.html',
controller: 'customers.modal'
});
modalInstance.result.then(function(selectedCustomer) {
console.log(selectedCustomer);
});
}
In the modal controller:
var controllerId = 'customers.modal';
angular.module('app').controller(controllerId,
['$modalInstance', customersModal]);
function customersModal($modalInstance) {
// Modal controller stuff
}
But when I do, I get the following error:
Unknown provider: $modalInstanceProvider <- $modalInstance
If I take out $modalInstance, it works but I obviously have no reference to the modal in the calling controller..
Edit
I don't know if it is worth noting, but I am using the Controller As syntax:
<div class="container-fluid" data-ng-controller="customers.modal as vm">
Application dependencies:
var app = angular.module('app', [
// Angular modules
'ngAnimate', // animations
'ngRoute', // routing
'ngSanitize', // sanitizes html bindings (ex: sidebar.js)
// Custom modules
'common', // common functions, logger, spinner
'common.bootstrap', // bootstrap dialog wrapper functions
// 3rd Party Modules
'ui.bootstrap', // ui-bootstrap (ex: carousel, pagination, dialog)
'breeze.directives', // breeze validation directive (zValidate)
]);
I've created a plunker which is showing the problem here: http://plnkr.co/edit/u8MSSegOnUQgsA36SMhg?p=preview
The problem was that you were specifying a controller in 2 places - when opening a modal and inside a template - this is not needed. Remove ng-controller from a template and things will work as expected:
http://plnkr.co/edit/khySg1gJjqz1Qv4g4cS5?p=preview
try this syntax first
angular.module('app').controller('customers.modal',
['$modalInstance', function($modalInstance){
// Modal controller stuff
}]);
I think it get messed up if you use bracket notation and declare the controller outside.
$modalInstance is the modalInstance you create there
var modalInstance = $modal.open({
templateUrl: 'app/customer/customers.modal.html',
controller: 'customers.modal'
});
it's really the same object. It get injected back in the controller but it's not a service/factory. So it doesn't have a Provider.
This is a tricky part in the lib. Feel free to ask to the original authors of ui-bootstrap. They have been helpful in explaining that.