Search code examples
javascriptangularjsdependency-injectionscopeangularjs-injector

Error Unknown provider: $scopeProvider <- $scope when using $injector to inject $scope into controller


When using DI in this fashion:

var MainController = function MainController($scope) {
  //use $scope here
};
MainController.$inject = ["$scope"];

It, work but, when it's used like this:

var MainController = function MainController($injector) {
  var $scope = $injector.get("$scope");
};
MainController.$inject = ["$injector"];

This will result in error:

Error: [$injector:unpr] Unknown provider: $scopeProvider <- $scope

Here's a plunker with an example that showcases the error, check the comments for an alternative to see that only the scope not custom services are affected by this.
I found this Angular bug where they talk about the controller being instantiated before the child $scope is being created as Tomer Avni answered, so:

  1. why the first way of injecting $scope work while the second doesn't?
  2. And is there a way to use the second method of Dependency injection with $injector to inject $scope?

Solution

  • I've replied to you on gitter, but answering here as well as it may help other who have the same issue.


    When you give Angular a function to call that has values that will be derived from dependency injection (e.g. a service, a controller, etc.), Angular will:

    • Look for an .$inject property on the function object which should be an array of dependency names as strings (e.g. ['$scope']).
    • If $inject is undefined, it will use the parameters of the function definition (which works in most cases, unless you minify your code and the names get mangled).

    So, in a nutshell, it will look up the names you have specified in the DI container.

    $scope doesn't exist in the DI container, only $rootScope does. So, if you're accessing the injector directly and asking for an instance of $scope, you'll get the injection error you've seen here.

    In the example where it worked, you're not accessing the injector directly but instead relying on Angular working out how to create your controller. That's a subtle difference, but in this case an important one. Within Angular, when creating the controller instance it will resolve $scope to the result of calling $rootScope.$new() (i.e. what you would do when manually instantiating the controller in a unit test).

    I hope that explains why your example didn't work.

    As for the second question, you could manually get a scope instance by doing something like:

    var $scope = $injector.get('$rootScope').$new();

    But now we're starting to go down a pretty murky path... It's quite atypical to inject the $injector directly. I'd avoid that if you can. Is there a reason you need to do this?