Может ли инжектор Angular $быть украшен $provision.decorator?
Возможно, это ужасная идея, но если это тогда, пожалуйста, скажите мне, почему и затем притворяйтесь, что это академическое упражнение, которое не увидит свет дня в производстве.
Я хотел бы добавить некоторую логику в службу инжектора Angular $, чтобы отслеживать, когда определенные службы вводятся в другие службы. Поскольку кажется, что Angular предоставляет механизм для оформления сервисов, я думал, что это будет путь. Однако следующий код вызывает ошибку.
(function () {
'use strict';
var app = angular.module('app');
app.config(['$provide', function ($provide) {
$provide.decorator('$injector', ['$log', '$delegate', addLoggingToInjector]);
}]);
function addLoggingToInjector($log, $delegate) {
var baseInstantiate = $delegate.instantiate;
var baseInvoke = $delegate.invoke;
$delegate.instantiate = function (type, locals) {
// $log.debug('Calling $injector.instantiate');
baseInstantiate(type, locals);
};
$delegate.invoke = function (fn, self, locals) {
// $log.debug('Calling $injector.invoke');
baseInvoke(fn, self, locals);
};
return $delegate;
};
})();
Конкретная ошибка:
Неподготовленная ошибка: [$ injector: modulerr] Не удалось создать приложение модуля из-за: Ошибка: [$ injector: unpr] Неизвестный поставщик: $injectorProvider
Ответы
Ответ 1
Вы не можете использовать службу декоратора Angular для инжектора $. Как отмечает Артур, $injector
немного отличается от других сервисов. Но мы можем создать наш собственный декоратор.
Почему мы не можем использовать Angular decorator
На уровне кода проблема заключается в том, что $injector
не имеет функции-конструктора - там нет $injectorProvider
.
Например, оба из них возвращают true:
$injector.has('$location');
$injector.has('$locationProvider')
Однако, пока это возвращает true:
$injector.has('$injector')
это возвращает false:
$injector.has('$injectorProvider')
Мы видим важность, когда смотрим на функцию Angular decorator:
function decorator(serviceName, decorFn) {
var origProvider = providerInjector.get(serviceName + providerSuffix),
orig$get = origProvider.$get;
origProvider.$get = function() {
var origInstance = instanceInjector.invoke(orig$get, origProvider);
return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
};
}
и
providerSuffix = 'Provider'
Итак, декоратор Angular рассчитывает работать с конструктором службы (serviceName + providerSuffix
). Прагматически , так как у нас нет $injectorProvider
, мы не можем использовать декоратор.
Решение
Что мы можем сделать, это переопределить функцию Angular инжектор get
, заменив инжектор по умолчанию get
на тот, который вызывает оригинал, Angular define, get
, за которым следует наша функция.
Мы применим это к $injector
, а не к несуществующим $injectorProvider
следующим образом:
app.config(['$provide','$injector', function ($provide,$injector) {
// The function we'll add to the injector
myFunc = function () {
console.log("injector called ", arguments);
};
// Get a copy of the injector get function
var origProvider = $injector,
origGet = origProvider.get;
//Override injector get with our own
origProvider.get = function() {
// Call the original get function
var returnValue = origGet.apply(this, arguments);
// Call our function
myFunc.apply(this,arguments);
return returnValue;
}
}]);
Вы увидите, что введенный провайдер является первым дополнением, поэтому app.value('aValue', 'something');
выводит следующий оператор журнала:
injector called ["aValueProvider"]
Демо-скрипт
Ответ 2
Ответ: no.
$provide.decorator
используется для создания перехвата службы - поэтому он вызывается из блока .config
, когда еще есть время для настройки всех служб, поскольку ни один из них не был создан, $provide.decorator
в основном получает Provider
сервиса и свопит его $get
с недавно доставленным decorFn
.
$injector
не похож на другие сервисы. Он создается, поскольку самый первый шаг bootstrapping
вызывает путь приложения до app.config
. [посмотреть функции: bootstrap
и createInjector
в angular исходный код]
Но эй, вы можете легко достичь своей цели, немного изменив исходный код:-) Особенно посмотрите на function invoke(fn, self, locals)
.
ОБНОВЛЕНИЕ Я получил вдохновение от @KayakDave. Фактически вам не нужно копать исходный код. Вы можете использовать следующий шаблон для наблюдения за каждым вызовом любого из методов $injector
:
app.config(['$injector', function ($injector) {
$injector.proper =
{
get : $injector.get,
invoke : $injector.invoke,
instantiate : $injector.instantiate,
annotate : $injector.annotate,
has : $injector.has
}
function getDecorator(serviceName)
{
console.log("injector GET: ", serviceName);
return this.proper.get(serviceName);
}
function invokeDecorator(fn, self, locals)
{
console.log("injector INVOKE: ", fn, self, locals);
return this.proper.invoke(fn, self, locals);
}
function instantiateDecorator(Type, locals)
{
console.log("injector INSTANTIATE: ", Type, locals);
return this.proper.instantiate(Type, locals);
}
function annotateDecorator (fn)
{
console.log("injector ANNOTATE: ", fn);
return this.proper.annotate(fn);
}
function hasDecorator(name)
{
console.log("injector HAS: ", name);
return this.proper.has(name);
}
$injector.get = getDecorator;
$injector.invoke = invokeDecorator;
$injector.instantiate = instantiateDecorator;
$injector.annotate = annotateDecorator;
$injector.has = hasDecorator;
}]);
PLNKR