I have this controller attached to a div:
.controller('ShowInverterConnectController', ['$scope', '$location', function($scope, $location) {
....
}])
I would like to use the $location argument of it.
I am currently doing this:
angular.element('.ng-scope').scope().$apply(function() {
console.log('test:', angular.element('.ng-scope').scope().$location);
});
But $location is coming out undefined, is there anyway to use $location?
Thanks
Not sure what you are trying to do with Angular service outside of the app, but I assume you have a good reason, because in many cases this will be considered bad practice. Anyway.
$location is not a property of the scope, so you can't get it like you are trying. However you can use $injector service to retrieve other services like $location:
angular.element('.ng-scope').scope().$apply(function() {
console.log('test:', angular.element('.ng-scope').injector().get('$location'));
});
Related
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;
])
I am wondering if there is a way to abstract out dependencies that are used across multiple Angular Controllers.
For example, if both my StudentCtrl and TeacherCtrl take advantage of $scope, $rootScope, $routeParams, and $http, is there a way to abstract these out into a package of some sort, say standardDependencies and then inject standardDependencies into both controllers instead of writing out all of the shared ones?
ex.
app.controller('StudentCtrl', ['standardDependencies', function(standardDependencies){
}]);
I know this is what services are typically used for I just haven't seen any examples for injecting things like $scope, only custom functions.
You can't do this for $scope because it might be different for each controller.
Actual singleton-like services, yes, you could technically wrap up into another service:
app.service('standardDependencies', ['$rootScope', ..., function($rootScope, ...) {
this.$rootScope = $rootScope;
...
}}]);
But I don't see any good reason to do so. If StudentCtrl and TeacherCtrl are really that similar, maybe they should both depend on something like PersonService that wraps common functionality.
This is probably bad practice, but you could define an array standardDependencies at the top of app.js and then use array.concat.
e.g.:
// app.js
standardDependencies = ['$rootScope', '$rootParams', '$location', '$http', '$translate']
// controller.js
app.controller('StudentCtrl', standardDependencies.concat(['$q', '$log', etc,
function($rootScope, ..., $log){ ... }]);
You still have to list them out as function parameters, but at least you don't have to list them twice, once as a string, once as a parameter.
Use some kind of provider; however, this is not very good practice as it might make it difficult for the angular digest cycle to manage these references. This goes against best practices for dependency injection.
If you would like your scopes to access dependencies all across, either a) do proper inheritance of your scopes or b) just do native injection.
I hope that helps. :D
When a webpage loads for the first time, controller of my directive needs to know what is the currently selected route.
After first load I can detect change using
$scope.$on('$routeChangeSuccess', function(){});
EDIT:
Answers below are valid but it didn't work for me because:
- I am new to AngularJS technology i didn't realize that, on our project, we are using custom route provider
- You need to inject 'ngRoute' in your module before you can use $route
You can use the $routeChangeSuccess callback signature
function(event, currentRoute, previousRoute) {}
PLUNKER
e.g.
$rootScope.$on('$routeChangeSuccess', function(event, current, previous) {
console.log(current.$$route); // displays current $route object
});
The current documentation does not show the callback signatures(IDK why) but you can see them on their github, see this line.
you need to inject $route dependency in your controller and then get the current route using $route.current
Plunker Example
Controller code snippet:
.controller('MainController', function($scope, $route, $routeParams, $location) {
//Here setting scope with $route object
$scope.$route = $route;
$scope.$location = $location;
$scope.$routeParams = $routeParams;
})
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.