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.
Related
I am using angular 1.5.6 and my application is composed of several modules, each module has his own view and controller. My question is how can I watch $rootScope on each of those controllers. I need to watch $rootScope on all controllers because I have a global variable on $rootScope.account and when user refresh the page $rootScope.account is cleared and all my application crash. I would like to watch $rootScope.account on all my modules, so when a user refresh I can handle the refresh and redirect to login page. I thought maybe of a service that I would inject on all each of my modules?
You can do it in two simple ways.
First: include $rootScope into your controller:
app.controller('MyCtrl', [
'$rootScope',
'$scope',
function($rootScope, $scope) {
$rootScope.$watch('myvar', function(newValue, oldValue) {
doSomething();
}
//...
}
]);
Second. If you don't have variables with same names in the other scopes then you can avoid using $rootScope and use just $scope. This will work because every $scope is inherited from $rootScope.
app.controller('MyCtrl', [
'$scope',
function($scope) {
$scope.$watch('myvar', function(newValue, oldValue) {
doSomething();
}
//...
}
]);
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
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
I have noticed that in one of my controllers I am getting $rootScope and $scope injected, and they both point to the same object.
Furthermore, in all my other controllers, the $scope object is shared. So whenever I inject scope, it contains the properties/methods assigned to it in all the other controllers that have so far been instantiated.
This isn't an app that I worked on from the beginning and it's pretty massive. I haven't seen this behavior before and I don't know where to begin diagnosing it. Any ideas what is causing this?
The way that we are setting up our controllers/directives is pretty standard and it looks like this:
angular.module('myApp')
.directive('mainNav', function() {
return {
restrict: 'A',
templateUrl: 'scripts/directives/mainNav/mainNav.html',
controller: 'mainNavCtrl',
replace: true,
link: function(scope, element) {
//Do DOM-related stuff
});
}
};
})
.controller('mainNavCtrl', function($rootScope, $scope, $state) {
//Do controller stuff
});
We do also configure our app as follows:
angular.module('myApp', ['ui.router', 'kendo.directives'])
.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('app', {
url: '/',
templateUrl: 'views/app.html',
resolve: {
//Fetch stuff
}
})
; });
In response to Kursad Gulseven's comment, this is what I'm seeing in Batarang:
The scope with ID 002 gets passed in as $scope and $rootScope to the first controller. When properties are added to $scope, they show up on $rootScope. Then all the other controllers are receiving the scope with ID 00A. So properties added to $scope in those controllers are visible to all other controllers getting $scope injected.
$rootScope and $scope are not the same object.
If you have an object named anObject in root scope and it has an attribute named anAttribute; then you can access that attribute using $scope like this:
$scope.anObject.anAttribute
That's because Angular looks up parent scopes if it cannot find an object in $scope.
UPDATE:
Your mainNav directive should have an inherited child scope by adding scope: true.
When "scope" is false, which is the default setting for directives, the parent controller and directive's controller share the same scope object. Setting scope true will create a new child scope for the directive.
This code is not working:
<td><img src="{{$rootScope.s3BucketUrl}}"/></td>
and this is where it's defined:
var appCtrl = app.controller('AppCtrl', function($scope, $resource, $location, $route, sharedProperties, $q, $rootScope){
defer = $q.defer();
//$rootScope.s3BucketUrl = 'http://lynd.s3.amazonaws.com/';//comment below one when live
$rootScope.s3BucketUrl = 'http://lynd-test.s3.amazonaws.com/';
//code omitted
});
AppCtrl is bound first on body tag.
Could anybody please suggest how do I print $rootScope variable?
Just use {{s3BucketUrl}} and you would get the value.
Your current scope is a child scope of rootscope and hence you can always access the elements of rootscope.
This wiki is highly recommended