AngularJS 两个不同的 $injectors
AngularJS two different $injectors
今天我发现,$injector
注入到配置或提供者不同于 $injector
注入到服务、工厂或控制器。
而来自这个 $injectors 的 get()
函数的工作方式不同。
$injector
来自配置或提供商,不能 get()
任何服务! $injector.get('myService')
抛出 Error: [$injector:unpr] Unknown provider: myService
,但 $injector.has('myService')
return 正确。太奇怪了。
$injector
来自服务或控制器正常工作。
为了更好地理解,这里有一个代码示例:
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();
}
}
});
谁能解释一下为什么有两个不同的注入器?
以及如何使用 provider/config 中的 $injector 注入服务(当然是在服务初始化之后)?
P.S。我使用 angular 1.3.13
好的。看了大家的评论,这是我的回答。
我在 plunk 中编辑代码以使其工作,当调用 providerInjector.get() 时,代码应如下所示:
$scope.getMyServiceFromProviderInjector = function () {
try {
myProvider.providerInjector.get('myServiceProvider');//here is change in provider name
return "OK";
} catch (e) {
return e.toString();
}
};
根据 angular docs 引用以下配置和 运行 块:
- Configuration blocks - get executed during the provider registrations and configuration phase. Only providers and constants
can be injected into configuration blocks. This is to prevent
accidental instantiation of services before they have been fully
configured.
- Run blocks - get executed after the injector is created and are used to kickstart the application. Only instances and constants can be
injected into run blocks. This is to prevent further system
configuration during application run time.
这只是意味着,您无法在配置块中获取服务实例。
我在 github 上发现了这个问题:https://github.com/angular/angular.js/issues/5559
In the config function, $injector is the provider injector, where in the run function, $injector is the instance injector.
One's the $injector at the config stage (only providers and constants accessible), and one's the $injector at the run stage. The confusion may be that you're thinking the $injector modifies itself to include the new stuff as it crosses the line from config to run, but that's not true. They're two separate (although related) objects, with their own caches of instances.
A more in-depth reason for this dichotomy will probably come from a deep learning of the $injector internals, but it seems like it's been DRY-ed pretty hardcore, and the two types of injectors share almost all the same behavior, except in how they deal with "cache misses" in their instance caches.
We are going to overhaul the injector in v2, so this will get fixed there (getting rid of the config phase is one of the objectives of the injector v2).
似乎真的有两个不同的注入器,angular 开发人员不会修复该行为(在版本 <2.0 中)。由于某种原因,没有人在 $injector 文档中添加关于该方面的注释。
我无法找到一种方法如何在不使用 hacky 技巧的情况下真正获得 instance 配置块中的注入器。所以,我写了一个可爱的供应商来解决这类问题。
.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){}])
我前段时间写过这篇文章,它解释了 AngularJS 的两个注入器的生命周期,即 providerInjector 和 instanceInjector。
http://agiliq.com/blog/2017/04/angularjs-injectors-internals/
今天我发现,$injector
注入到配置或提供者不同于 $injector
注入到服务、工厂或控制器。
而来自这个 $injectors 的 get()
函数的工作方式不同。
$injector
来自配置或提供商,不能 get()
任何服务! $injector.get('myService')
抛出 Error: [$injector:unpr] Unknown provider: myService
,但 $injector.has('myService')
return 正确。太奇怪了。
$injector
来自服务或控制器正常工作。
为了更好地理解,这里有一个代码示例:
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();
}
}
});
谁能解释一下为什么有两个不同的注入器?
以及如何使用 provider/config 中的 $injector 注入服务(当然是在服务初始化之后)?
P.S。我使用 angular 1.3.13
好的。看了大家的评论,这是我的回答。
我在 plunk 中编辑代码以使其工作,当调用 providerInjector.get() 时,代码应如下所示:
$scope.getMyServiceFromProviderInjector = function () {
try {
myProvider.providerInjector.get('myServiceProvider');//here is change in provider name
return "OK";
} catch (e) {
return e.toString();
}
};
根据 angular docs 引用以下配置和 运行 块:
- Configuration blocks - get executed during the provider registrations and configuration phase. Only providers and constants can be injected into configuration blocks. This is to prevent accidental instantiation of services before they have been fully configured.
- Run blocks - get executed after the injector is created and are used to kickstart the application. Only instances and constants can be injected into run blocks. This is to prevent further system configuration during application run time.
这只是意味着,您无法在配置块中获取服务实例。
我在 github 上发现了这个问题:https://github.com/angular/angular.js/issues/5559
In the config function, $injector is the provider injector, where in the run function, $injector is the instance injector.
One's the $injector at the config stage (only providers and constants accessible), and one's the $injector at the run stage. The confusion may be that you're thinking the $injector modifies itself to include the new stuff as it crosses the line from config to run, but that's not true. They're two separate (although related) objects, with their own caches of instances.
A more in-depth reason for this dichotomy will probably come from a deep learning of the $injector internals, but it seems like it's been DRY-ed pretty hardcore, and the two types of injectors share almost all the same behavior, except in how they deal with "cache misses" in their instance caches.
We are going to overhaul the injector in v2, so this will get fixed there (getting rid of the config phase is one of the objectives of the injector v2).
似乎真的有两个不同的注入器,angular 开发人员不会修复该行为(在版本 <2.0 中)。由于某种原因,没有人在 $injector 文档中添加关于该方面的注释。
我无法找到一种方法如何在不使用 hacky 技巧的情况下真正获得 instance 配置块中的注入器。所以,我写了一个可爱的供应商来解决这类问题。
.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){}])
我前段时间写过这篇文章,它解释了 AngularJS 的两个注入器的生命周期,即 providerInjector 和 instanceInjector。
http://agiliq.com/blog/2017/04/angularjs-injectors-internals/