Подтвердить что ты не робот

Может ли инжектор 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

4b9b3361

Ответ 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