Is possible to inject a controller into another controller that is part of the same module?
example:
var app = angular.module('myAppModule', [])
.controller('controllerOne', ['$scope', function($scope){
$scope.helloWorld = function(){
return 'Hello World';
}
}])
.controller('controllerTwo', ['$scope', 'controllerOne', function($scope, controllerOne){
console.log(controllerOne.helloWorld());
}])
I keep getting unknown provider on controllerOne. I don't see how that's possible since they coexist in the same module. Any help would be much appreciated.
You need to use $controller dependency by using that you can inject one controller to another
.controller('controllerTwo', ['$scope', '$controller', function($scope, $controller){
$controller('controllerOne', {$scope: $scope})
//inside scope you the controllerOne scope will available
}]);
But do prefer service/factory to share data
Move your logic into a "service" (either a factory/service/provider). I personally prefer factories, I just like controlling my own object instead of using this or anything like that with the other options.
Using a service, you give yourself the ability to abstract business logic from your controllers, and make that logic -- reusable -- !
var app = angular.module('myAppModule', [])
// typically people use the word Service at the end of the name
// even if it's a factory (it's all the same thing really...
.factory('sharedService', function () {
var methods = {};
methods.helloWorld = function () {
return 'Hello World!';
};
// whatever methods/properties you have within this methods object
// will be available to be called anywhere sharedService is injected.
return methods;
})
Notice sharedService is injected
.controller('ControllerOne', ['$scope', 'sharedService', function($scope, sharedService) {
$scope.helloWorld = sharedService.helloWorld();
}])
// Notice sharedService is injected here as well
.controller('ControllerTwo', ['$scope', 'sharedService', function($scope, sharedService){
// Now we can access it here too!
console.log( sharedService.helloWorld() );
}]);
Side note : Controllers should be capitalized to show their significance!
The power of services :)
If the a controllerTwo needs to call the same function as controllerOne, you may want to create a service to handle it. Angular Services - they are accessible throughout your program through dependency injection.
var app = angular.module('myAppModule', [])
.controller('controllerOne', ['$scope', 'Hello', function($scope, Hello){
console.log(Hello.helloWorld() + ' controller one');
}])
.controller('controllerTwo', ['$scope', 'Hello', function($scope, Hello){
console.log(Hello.helloWorld() + ' controller two');
}])
.factory('Hello', [function() {
var data = {
'helloWorld': function() {
return 'Hello World';
}
}
return data;
}]);
Hope this helps!
You cannot inject controllers in another controllers,only serviceProviers are injectable.That's the reason you are getting error as unkown provider in controller one.
Use Services instead and inject them in controllers,if there is some come functionality to be shared among controllers.Services are the best way to share data in between controllers.
You can declare a variable or function or say object on $rootScope, it's exists in your whole application.
Share data between AngularJS controllers
Related
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.
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 have an administrator login page. When a user successfully logs in as an admin, I want to display at the top of all pages the user visits:
<!-- nav bar -->
<div>
<span ng-show="$root.isAdmin">(ADMIN)</span>
</div>
I tried using $rootScope to accomplish this, but I keep getting undefined:
// controller for logging in
app.controller('AdminLoginCtrl', [ '$rootScope', '$scope', 'stateParams', 'loginService', function($rootScope, $scope, $stateParams, loginService) {
loginService.success(function() {
$rootScope.isAdmin = true;
console.log($rootScope.isAdmin); // returns true
$location.path("/login_success");
});
}]);
// some other controller AFTER user is logged in as admin
app.controller('BlahCtrl', ['$rootScope', '$scope', function($rootScope, $scope) {
console.log($rootScope.isAdmin); // returns 'undefined'
});
There have been some other similar topics, but don't apply to me:
(I don't have an injection problem):
$rootscope value undefined in Controller
(My other controller is definitely being called AFTER login, so $rootScope.isAdmin should be set):
AngularJS - Cannot access RootScope
(I don't have an ordering problem):
Angular $rootScope.$on Undefined
The other solution could be to not use $rootScope at all, and just use a service to hold shared data. But if I have 10+ controllers, is it acceptable to do this in each:
app.controller('...', ['$scope', 'checkAdminService', function($scope, checkAdminService) {
$scope.isAdmin = checkAdminService.getIsAdmin()
}]);
Then:
<span ng-show="isAdmin">(ADMIN)</span>
Is this acceptable or more cumbersome? And for completeness sake, why doesn't $rootScope solution work at all?
It's indeed a bad practice using the $rootScope. The best solution is what you propose yourself to create a service storing the information you want to share between different controllers. It is no problem to call this method on several locations...
Why don't you define a separate controller for the top-part of your page which will be the only one verifying whether the user is an admin? The other part of your page can be controller by different controllers.
This example will explain how to use several controllers, but of course this can also be achieved via Routers.
What if you replace the $rootScope that you're passing around with a special-purpose angular value that you can register at startup and pass around as needed?
http://plnkr.co/edit/r6kdouh0BbnYA4DmEcEM?p=preview
angular
.module('app')
.value('userProfile', userProfile);
function userProfile() {
var userProfile = {
isAdmin: false
};
return userProfile;
}
/////////////////
angular
.module('app')
.run(run);
run.$inject = ['userProfile', 'userAuthService'];
function run(userProfile, userAuthService) {
userAuthService.isUserAdmin().then(function(result) { userProfile.isAdmin = result; });
}
angular.module('yourmodule', [])
.run(['$rootScope', function($rootScope) {
$rootScope.isAdmin = true;
])
In this ng-book JSBin, does $scope.$watch() resolves to $rootScope.$watch() due to prototypal inheritance.
Can we inject $rootScope explicitly inside the controller so that $scope would be same as $rootScope inside the controller, without going through prototypal inheritance?
Replicating code here for reference:
// open this example and type person.name into the test field
angular.module('myApp', [])
.controller('MyController',
['$scope', '$parse', function($scope, $parse) {
$scope.person = {
name: "Ari Lerner"
};
$scope.$watch('expr', function(newVal, oldVal, scope) {
if (newVal !== oldVal) {
// Let's set up our parseFun with the expression
var parseFun = $parse(newVal);
// Get the value of the parsed expression, set it on the scope for output
scope.parsedExpr = parseFun(scope);
}
});
}]);
Just inject it the same way as $scope or $parse, anything defined on $rootScope will be then accessible inside your controller.
app.controller('MyController', ['$scope', '$parse', '$rootScope',
function($scope, $parse, $rootScope) {
$rootScope.foo();
console.log($rootScope.bar);
}
]);
etc.
If you intend to use the rootScope so badly, it has a provider just as scope does. Including '$rootScope' into your controller as you do with the '$scope' does the trick.
There's also the $parent attribute of the $scope which could come in handy, but IMO it tends to make code less maintainable if abused. In particular when multiple scopes are nested, as you need to traverse the whole hierarchy.
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;
}]);