Kuan
Kuan

Reputation: 11389

Is $scope dependecy injection in AngularJS controller definition?

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

Answers (1)

DarkChipolata
DarkChipolata

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.

Update : difference between $scope and services

Take 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

Related Questions