Well I have seen couple of questions in StackOverFlow related to this but couldn't find proper answer.
Say in my app.js file I have defined a constants object which basically has controllers names. As I use "ui-router" I want to attach Controllers to views on state change.
app.js
var app = angular.module('myApp', ['ui.router']);
app.constant('CONTROLLER', {
ONE: 'ControllerOne',
TWO: 'ControllerTwo'
});
Now I want to use those constants to define Controller names in other file, say controller.js. Illustrating couple of ways which I tried but dint work for me.
controller.js
var app = angular.module('myApp', ['CONTROLLER']);
app.controller(CONTROLLER.ONE, ['$scope', 'myFactory', function ($scope, myFactory) {
$scope.result = myFactory.someAPI();
}]);
ERROR -> Uncaught ReferenceError: CONTROLLER is not defined
Even I tried injecting that constant module in Controller which gave same error.
var app = angular.module('myApp', ['CONTROLLER']);
app.controller(CONTROLLER.ONE, ['$scope', 'myFactory', 'CONTROLLER', function ($scope, myFactory, CONTROLLER) {
$scope.result = myFactory.someAPI();
}]);
I know that second way is wrong. As I define controller names in my main app.js file and use those constants to attach controllers to views during state change.
I want reuse those constants to define controllers names too.
I may be doing something wrong. Any suggestions ?
Your code shows that you have correctly injected the CONTROLLER constant into the controller constructor function.
var app = angular.module('myApp', ['CONTROLLER']);
app.controller(CONTROLLER.ONE, ['$scope', 'myFactory', 'CONTROLLER', function ($scope, myFactory, CONTROLLER) {
// use constant here
}]);
That being said you can only access the constant except from within the controller function itself. The names of controllers, services, factories etc must be accessible strings, which is not the case until the constant has been injected.
You could use a plain old JavaScript object outside angular to define the names.
var CONTROLLER = {
ONE: 'ControllerOne',
TWO: 'ControllerTwo'
});
I can only guess that you are trying to do this because the controller or directive you are writing will be referenced/used at multiple points throughout your html.
Related
I have an MVC application that has a master layout that has a #RenderBody() inside of it that renders the body of the individual page. In the master layout I have given the body tag an angularjs app name and controller name.
I want the controller to be defined in a separate global angular.js file. This will share common functions that every page can take advantage of.
myApp.controller('myController', ['$scope', '$http', function ($scope, $http) {
$scope.sharedFunction = //...shared function stuff
})';
I also want to add specific scope functions and variables in the child pages in MVC. When I get to the child pages, I believe I end up redefining the controller if I use the same syntax.
myApp.controller('myController', ['$scope', '$http', function ($scope, $http) {
$scope.showOverlay = false;
}]);
How can I make it so the same controller that is defined in the body gets page specific items added to it in the child pages that are rendered using #RenderBody()?
You need to define your 'app' as a module in Angular, like so:
var app = angular.module("app", [myController])
The 2nd part of the declaration allows you to pass in an array of 'dependencies' that your Angular app relies on. In this case, your app will include your 'myController' controller
Then use this attribute on the part of your html that you want Angular to act upon:
ng-app="app"
Doing this will make your controller available to any page that utilises your 'app' module.
I got this to work by nesting ng-controllers. The second controller inherited all of the first controller functions and I was able to overwrite them and add to them in the second controller.
I have the following new service:
var SignatureService = function ($scope) {
this.announce = function () {
alert($scope.specificName);
}
};
SignatureService.$inject = ['$scope'];
This is declared for the app as follows:
MyAngularApp.service('SignatureService', SignatureService);
Several other services are added to the app in exactly the same way, and they all seem to work OK. I then have a controller declared and defined as follows:
MyAngularApp.controller('MyController', MyController);
...
var MyController = function ($scope, Service1, Service2, $location, $modal, SignatureService) {
...
}
MyController.$inject = ['$scope', 'Service1', 'Service2', '$location', '$modal', 'SignatureService'];
I am simply using the slightly unconvcentionaly manner of defining the servivce and injecting it that is standard in the app I am working on, as this works for all existing services, and I would prefer to simply slot mine in as per standard.
When the controller loads, I get an [$injector:unpr] in the browser console, with the error info:
$injector/unpr?p0=$scopeProvider <- $scope <- SignatureService
You can't inject $scope into your custom service. It just doesn't make sense since SignatureService can be injected anywhere including other services and other controlles. What $scope is supposed to be if you say inject it into two nested controllers, which one should be injected?
Scope object ($scope) is always associated with some DOM node, it is attached to it. That's why you see $scope in controllers and directives. And this is the reason why you can't have it in service: services are not related to specific DOM elements. Of course you can inject $rootScope but this is unlikely what you need in your question.
Summary: $scope is created from the $rootScope and injected in necessary controllers, but you can't injected it into custom service.
UPD. Based on comments you want to use service to define reusable controller methods. In this case I would go with what I call mixin approach. Define methods in the service and mix them in the necessary controllers.
app.service('controllerMixin', function() {
this.getName = function() {
alert(this.name);
};
});
and then extend controller scope with angular.extend:
app.controller('OneController', function($scope, controllerMixin) {
angular.extend($scope, controllerMixin);
// define controller specific methods
});
app.controller('TwoController', function($scope, controllerMixin) {
angular.extend($scope, controllerMixin);
// define controller specific methods
});
This is pretty effective, because controllerMixin doesn't have any notion of $scope whatsoever, when mixed into controller you refer to the scope with this. Also service doesn't change if you prefer to use controllerAs syntax, you would just extend this:
angular.extend(this, controllerMixin);
Demo: http://plnkr.co/edit/ePhSF8UttR4IgeUjLRSt?p=info
I was wondering if there is a way to expand my angular controller on the fly according to the user's permissions.
For example lets say that we have three different permission levels respectively (Admin,Supervisor,User) and an angular controller
app.controller("myController", ["$scope", function($scope){
$scope.userFunc = function(){....};
$scope.supervisorFunc = function(){....};
$scope.adminFunc = function(){....};
}]);
I would like to add the supervisorFunc and the adminFunc only if the user possest the right permission.
User controller will contain only userFunc
Supervisor will contain userFunc and supervisorFunc
Admin will contain all the methods (userFunc,supervisorFun and adminFunc)
Now I would like to add a separate js file that will inject the additional methods into the controller, so if you are a Supervisor your client will receive two js files, one is the main myController and an additional Supervisor Injector that will expand myController.
One important thing is that I don't want to dynamically generate myController js file at server side because it's bad practices.
any ideas ?
I like the idea, and we do the same thing (sort of).
IF the second set of controller items does not have new requirements (directives and such), you should be able to do this:
in app.js:
var app = angular.module('myModule', ["whatever-directive", "some-service"]);
app.config(['$httpProvider', '$locationProvider', function ($httpProvider, $locationProvider) {
..
..
}]);
..
..
in controllers.js
function someCtrl($scope, $location, $http) {...}
function someOtherCtrl($scope, $location, $http) {...}
in onlyForYouControllers.js - that is only added if needed
function someCtrlOnlyForYou($scope, $location, $http) {...}
Like I wrote - you must declare what ever needed in the app to start with - even if it not used by a particular set of controllers.
I am working on a trivial task (that I got working) which adds an an .active class to dom elements in a tab nav. Simple. Got it working by following https://stackoverflow.com/a/12306136/2068709.
Reading over the answer provided and comparing their controller to example controllers on angular's website I came across something interesting that I don't quite understand.
On the AngularJS website, $scope and $location (and other services) are injected into the controller along with an anonymous function which defines the controller. But on the answer, the services are not injected, rather they are passed via the anonymous function. Why does this work? Shouldn't the service always be injected?
In terms of an example: Why does this
angular.module('controllers', [])
.controller('NavigationController', ['$scope', '$location', function($scope, $location) {
$scope.isActive = function(route) {
return route === $location.path();
};
}])
and this
angular.module('controllers', [])
.controller('NavigationController', function($scope, $location) {
$scope.isActive = function(route) {
return route === $location.path();
};
})
both work?
This may be trivial but its throwing my brain for a loop that I can't figure out.
The two examples are equivalent - they just make use of different syntax. The first example uses what they call "inline array annotation" (see here). The purpose of this alternate syntax is just to allow a convenient way to make the injected variable names different than the name of the dependency.
So for example, if you wanted the variable names to be "s" and "l", then you could write:
angular.module('controllers', [])
.controller('NavigationController', ['$scope', '$location', function(s, l) {
s.isActive = function(route) {
return route === l.path();
};
}]);
Actually they are injected in both cases, the difference between those two cases is in the first scenario you define and name the dependency this could be useful if you minify your js code and that way you are declaring explicitly the dependency for examply it could be:
angular.module('controllers', [])
.controller('NavigationController', ['$scope', '$location', function($s, $l) {
$s.isActive = function(route) {
return route === $l.path();
};
}])
that way angular will know which dependency to inject on which parameter without looking at the naming for each parameter.
the other case you need to be explicit and declare which dependency you'll inject by setting up the name.
I hope that helps.
This is how angular handles code minification. by keeping strings intact it can keep mapping vars when they are renamed.
if you take a look at the code of the controller https://github.com/angular/angular.js/blob/master/src/ng/controller.js#L48
you'll see that the constructor can accept both function and array.
Suppose I want to make this a variable a constant to be shared among controllers in Angularjs;
$webroot = "localhost/webroot/app"
After some investigation, it seems services are the way to do. But what is the best way? Do I use a factory, service, value or something else?
The services.js from angularjs-master-seed is below;
angular.module('myApp.services', []).value('version', '0.1');
How do I modify it to have a constant $webroot that is sharable among controllers?
Can I do the following?
angular.module('myApp.services', [])
.value('version', '0.1')
.constant('webroot','localhost/webroot/app');
If it is ok, how do I call it in the controller?
Whats happens when you want more constants? How about adding a config object that you can inject wherever needed. As its a single file it's also much easier to have dev.config and prod.config files that can be swapped in and out at build time.
app.factory('Config', function(){
return{
webRoot: "localhost/webroot/app",
moreSettings: "abc"
};
});
If your variable have a constant value or is set once value is the right choice.
You can define it like this:
app = angular.module('myApp', []);
app.value('$webroot', 'localhost/webroot/app');
Now you can inject the service to your controller and use it:
app.controller('myController', ['$scope', '$webroot', function($scope, $webroot) {
$scope.webroot = $webroot;
}]);
Edit #1
To fit your updated question: You can use a constant the same way as a value:
app = angular.module('myApp', []);
app.constant('$webroot', 'localhost/webroot/app');
app.controller('myController', ['$scope', '$webroot', function($scope, $webroot) {
$scope.webroot = $webroot;
}]);