Reputation: 11389
All:
What confused me about dependency injection in AngularJS is its concept(I guess I have still not get the whole idea of DI):
I wonder how to tell which are dependencies that should be(and can be) injected?
For example:
If I define a controller and directive:
app.controller("maincontroller", ["$scope", "dependency1", function($scope, dependency1){
}])
app.directive("dir1", ["dependency2", "dependency3",
function(dependency2, dependency3){
return {
restrict: "AE",
scope:{},
controller: function($scope){},
link:function(scope, EL, attrs, ctrl){}
};
}])
I wonder why I need to inject $scope as dependency in controller, but no need to do that in directive definition. And what is more: if I put $scope in that directive function, it will gives me an error:
Error: error:unpr
Unknown Provider
Unknown provider: $scopeProvider <- $scope <- dir1Directive
[1] Is this $scope a dependency, or [2] my understanding about dependency is totally wrong(someone told me that $scope is not a dependency) or [3] my understanding about directive def function is wrong(only service can be put in directive def function)?
If my wrong is the last one, then how many type of dependencies does AngularJS have?
Thanks
Upvotes: 2
Views: 1529
Reputation: 965
$scope
isn't actually a service !This is why you don't have to pass it as a dependency of the directive. $scope
is a local passed to the injector, a value passed to the function, as if i would do console.log(myValue)
. myValue
is not a service.
The $injector
's invoke
method, which passes services to a function, can replace a requested dependency by a given value, see the docs for more details. I recognize that mixing services and simples values in the arguments list are confusing, as we can't know which one are the services and which one are simple arguments, but it works that way.
We can consider that something like this is executed when a new controller is instanciated :
var $rootScope = $injector.get('$rootScope');
$injector.invoke(controller, context /*the this binding*/, {
$scope: $rootScope.new()
});
See also the code of the controller service for further details.
$scope
and servicesTake this function :
function add(a, b) {
return a + b;
}
a
and b
are "simple arguments", they are the values on which the function perform calculations. Imagine that you want to broadcast a message to your app that an addition just have been performed. In Angular, we can use the $rootScope.$broadcast
method.
function add(a, b) {
$rootScope.$broadcast('addition.done', a + b);
return a + b;
}
But here, $rootScope
has never been declared. We have to load the $rootScope
service in our function. Angular uses the $injector.invoke
method in order to do this : it takes a function or an array (the "square bracket" notation), extract the services names from the function arguments/array elements, and calls the function with the corresponding services passed as arguments.
function add(a, b, $rootScope) {
$rootScope.$broadcast('addition.done', a + b);
return a + b;
}
var $injector = angular.injector(['ng']); // Creates the injector, with the services of `ng` module.
$injector.invoke(add); // Calls the function with the requested services
It will throw an error, because a
and b
aren't services. In fact, they don't have to be services, because they are values that we want to set ourself. For the injector, they are locals. In order to perform the addition of 2
and 3
with the add
function, we have to do the following :
function add(a, b, $rootScope) {
$rootScope.$broadcast('addition.done', a + b);
return a + b;
}
var $injector = angular.injector(['ng']);
$injector.invoke(add, this, {
a: 2, // When requesting the `a` service, return the value `2`
b: 3 // The same here, with `b` and `3`
});
The same is done with controllers. $scope
itself is not a service, but a new scope, build each time the directive is loaded. Like a
and b
, the newly created $scope
is a value on which the function perform some logic and modifications. $scope
is not a dependency, but an argument of the function. And because the $injector
extracts the dependencies from the arguments list, we can't tell if an argument is a service or not (except if you already know it, but this isn't an evidence). Here, $scope
is not a service, and is also the same object that in the link
function in your directive. Note the fact that the documentation does not name the scope argument $scope
, but scope
. If scope
was a service, it would throw an error, but actually it doesn't, because the link
function isn't called with $injector.invoke
.
Upvotes: 4