Corner JS two different $ injectors

Today I discovered that the $injector entered in config or provider is different from the $injector entered in a service, factory, or controller.

And the get() function of these $ injectors works differently.

$injector from configuration or provider, cannot get() any service! $injector.get('myService') throws Error: [$injector:unpr] Unknown provider: myService , but $injector.has('myService') returns true. It is very strange.

$injector from the service or controller is working fine.

Here is a sample code for better understanding:

 angular.module('app', []) .provider('myProvider', function ($injector) { this.$get = ['$injector', function (serviceInjector) { return { providerInjector: $injector, serviceInjector: serviceInjector }; }]; }) .service('myService', function () {}) .controller('myCtrl', function ($scope, myProvider) { var providerInjector = myProvider.providerInjector; var serviceInjector = myProvider.serviceInjector; console.log(providerInjector === serviceInjector); // -> false console.log(serviceInjector.has('myService')); // `serviceInjector` has `myService` console.log(getMyService(serviceInjector)); // `serviceInjector` can get `myService` console.log(providerInjector.has('myService')); // `providerInjector` has `myService` too! console.log(getMyService(providerInjector)); // but `providerInjector` can't get `myService`! =( function getMyService(injector) { try { injector.get('myService'); return "OK"; } catch (e) { return e.toString(); } } }); 

Here is the plunker to play

Can anyone explain why there are two different injectors?

And how can I use the $ injector from the / config provider to inject the service (after the service has been initialized, of course)?

PS I am using angular 1.3.13

+6
source share
3 answers

I found this problem on github: https://github.com/angular/angular.js/issues/5559

In the configuration function, $ injector is the injector of the provider, where in the start function $ injector is the instance injector.

One $ injector at the configuration stage (only suppliers and constants are available) and one $ injector at the launch stage. The confusion may be that you think that $ injector modifies itself to include new material when it crosses the line from config to run, but it is not. These are two separate (albeit related) objects with their own instance caches.

The deeper reason for this dichotomy is probably due to a deep study of the internal components of the injector, but it seems to be pretty hardcore, and the two types of injectors have almost all the same behavior, except for how they deal with “cache misses” in their instance caches.

We are going to carry out a major overhaul of the injector in v2, so it will be fixed there (getting rid of the configuration phase is one of the goals of the v2 injector).

It seems that there really are two different injectors, and angular developers will not fix this behavior (in versions 2.0). And no one added a note about this aspect to the $ injector docs for some reason.

I could not find a way to really get the injector injector inside the configuration block without hacking tricks. So, I am writing a cute provider to solve such problems.

 .provider('instanceInjector', function () { var instanceInjector; function get() { return instanceInjector; } function exists() { return !!instanceInjector; } angular.extend(this, { get: get, exists: exists }); this.$get = function ($injector) { instanceInjector = $injector; return { get: get, exists: exists }; } }) // We need to inject service somewhere. // Otherwise $get function will be never executed .run(['instanceInjector', function(instanceInjector){}]) 
+8
source

Ok After reading your comments, here is my answer.

I edited the code in plunk to make it work, when calling providerInjector.get (), the code should be as follows:

 $scope.getMyServiceFromProviderInjector = function () { try { myProvider.providerInjector.get('myServiceProvider');//here is change in provider name return "OK"; } catch (e) { return e.toString(); } }; 

According to angular docs for configuration and startup codes, the following is indicated:

  • Configuration Blocks — Run during the registration and configuration of the provider. Only suppliers and constants can be entered in configuration blocks. This is to prevent accidental provision of services before they are fully configured.
  • Run blocks - execute after creating an injector and use it to start the application. Only instances and constants can be injected into trigger blocks. This is to prevent further system configuration during application execution.

It just means that you cannot receive service instances inside configuration blocks.

+1
source

I wrote this a while ago, which explains the life cycle of both AngularJS injectors, such as providerInjector and instanceInjector.

http://agiliq.com/blog/2017/04/angularjs-injectors-internals/

0
source

Source: https://habr.com/ru/post/986099/


All Articles