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.
Related
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');"
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!
If I have a utility function foo that I want to be able to call from anywhere inside of my ng-app declaration. Is there someway I can make it globally accessible in my module setup or do I need to add it to the scope in every controller?
You basically have two options, either define it as a service, or place it on your root scope. I would suggest that you make a service out of it to avoid polluting the root scope. You create a service and make it available in your controller like this:
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.angularjs.org/1.1.2/angular.min.js"></script>
<script type="text/javascript">
var myApp = angular.module('myApp', []);
myApp.factory('myService', function() {
return {
foo: function() {
alert("I'm foo!");
}
};
});
myApp.controller('MainCtrl', ['$scope', 'myService', function($scope, myService) {
$scope.callFoo = function() {
myService.foo();
}
}]);
</script>
</head>
<body ng-controller="MainCtrl">
<button ng-click="callFoo()">Call foo</button>
</body>
</html>
If that's not an option for you, you can add it to the root scope like this:
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.angularjs.org/1.1.2/angular.min.js"></script>
<script type="text/javascript">
var myApp = angular.module('myApp', []);
myApp.run(function($rootScope) {
$rootScope.globalFoo = function() {
alert("I'm global foo!");
};
});
myApp.controller('MainCtrl', ['$scope', function($scope){
}]);
</script>
</head>
<body ng-controller="MainCtrl">
<button ng-click="globalFoo()">Call global foo</button>
</body>
</html>
That way, all of your templates can call globalFoo() without having to pass it to the template from the controller.
You can also combine them I guess:
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.angularjs.org/1.1.2/angular.min.js"></script>
<script type="text/javascript">
var myApp = angular.module('myApp', []);
myApp.factory('myService', function() {
return {
foo: function() {
alert("I'm foo!");
}
};
});
myApp.run(function($rootScope, myService) {
$rootScope.appData = myService;
});
myApp.controller('MainCtrl', ['$scope', function($scope){
}]);
</script>
</head>
<body ng-controller="MainCtrl">
<button ng-click="appData.foo()">Call foo</button>
</body>
</html>
Though the first approach is advocated as 'the angular like' approach, I feel this adds overheads.
Consider if I want to use this myservice.foo function in 10 different controllers. I will have to specify this 'myService' dependency and then $scope.callFoo scope property in all ten of them. This is simply a repetition and somehow violates the DRY principle.
Whereas, if I use the $rootScope approach, I specify this global function gobalFoo only once and it will be available in all my future controllers, no matter how many.
AngularJs has "Services" and "Factories" just for problems like yours.These are used to have something global between Controllers, Directives, Other Services or any other angularjs components..You can defined functions, store data, make calculate functions or whatever you want inside Services and use them in AngularJs Components as Global.like
angular.module('MyModule', [...])
.service('MyService', ['$http', function($http){
return {
users: [...],
getUserFriends: function(userId){
return $http({
method: 'GET',
url: '/api/user/friends/' + userId
});
}
....
}
}])
if you need more
Find More About Why We Need AngularJs Services and Factories
I'm a bit newer to Angular but what I found useful to do (and pretty simple) is I made a global script that I load onto my page before the local script with global variables that I need to access on all pages anyway. In that script, I created an object called "globalFunctions" and added the functions that I need to access globally as properties. e.g. globalFunctions.foo = myFunc();. Then, in each local script, I wrote $scope.globalFunctions = globalFunctions; and I instantly have access to any function I added to the globalFunctions object in the global script.
This is a bit of a workaround and I'm not sure it helps you but it definitely helped me as I had many functions and it was a pain adding all of them to each page.
Angular Structural Question
I am new to angular.js and am just wondering how to go about performing a certain situation.
So basically, what I have got is a container:
<div ng-controller="ContainerController">
<container></container>
</div>
And the container controller and directives.
<script type="text/javascript" src="ContainerController.js"></script>
<script type="text/javascript" src="ContainerDirectives.js"></script>
Now the directives replaces the <container> tag with an example html: <example>{{ data }}</example>
Now within the scope of the ContainerController I have defined data as a string. (This is all example purposes). However when the directive accesses replaces it, it is unable to find the variable, due to scope.
The reason that this happens is because the ContainerDirective script's scope is not within the ContainerController scope. Meaning it is unable to access the variable.
Im just not sure on structure practices for these kinds of situations. Where do I put everything so the ContainerDirective can access the ContainerController scope.
I hope i have explained everything good enough
EDIT:
Test.js
(function(){
angular.module('test', []);
})();
TestController.js
(function(){
angular
.module('test')
.controller('TestController', [
'$scope',
TestController
]);
function TestController($scope) {
$scope.test = 'test';
}
})();
TestDirective.js
(function(){
angular.module('test').directive('test', function () {
return {
replace: true,
templateUrl: 'src/test/view/test.html',
link: function (scope, element, attrs) {
}
};
});
})();
test.html
<example>ClickMe</example>
index.html - body
<body ng-app="App" layout="row" ng-controller="TestController as page">
<test></test>
<script src="src/test/Test.js"></script>
<script src="src/test/TestController.js"></script>
<script src="src/test/TestDirective.js"></script>
<script type="text/javascript">
angular
.module('App', ['test']);
</script>
</body>
For reasons I have renamed certain variables and deleted a lot of data, but this is the core, and I am struggling to see anything wrong with this.
Error: [$interpolate:noconcat] Error while interpolating: abc/{{test}}
Strict Contextual Escaping disallows interpolations that concatenate multiple expressions when a trusted value is required.
So I figured out what was wrong in the end. Basically angular wont allow iframe of another location to be printed unless you first:
Give them the full url.
Then allow external url as a trusted website.
TO do this I had to basically add:
in the Test.js
angular.module('test', []).config(function($sceDelegateProvider) {
$sceDelegateProvider.resourceUrlWhitelist($sceDelegateProvider.resourceUrlWhitelist().concat([
'http://www.test.com/**'
]));
});
This basically took my whitelist and concatinated the new url to it.
Then inside test.html:
<example><iframe ng-src={{src}}></iframe></example>
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?