AngularJS中的Provider依赖注入 - 在提供者中使用$log

13

问题很简单:使用AngularJS时,我们无法将 $log 注入到提供程序中。

angular.module('my.module', [])
    .provider('myProvider', function ($log, $logProvider) {
        $log.log("Aloha!"); // Unknown provider: $log
        $logProvider.log("Hi!"); // undefined is not a function: $logProvider has no `log` method 
        this.$get = function($log) {
            $log.log("Hello!"); // Everything is ok here
        };
    });

是的,我们可以注入$logProvider,但它没有必要的方法(.log.error等)。

是的,我们可以注入$logProvider,然后手动调用$logProvider.$get(),但我们将无法使用修饰器中的额外逻辑。

是的,我们可以编写自己的logProvider,但我想知道为什么Angular不支持这个功能。

所以,在提供程序中我们不能"真正的使用 Angular 方式"来使用控制台?这个事实非常奇怪。而且很伤心。

问题:我需要如何在提供程序中"真正的使用 Angular 方式"来使用控制台?

我找不到关于这个问题的任何解释。 Angular Developer's Guide说我们需要在所有地方使用 $log 而不是控制台。

2个回答

16

提供程序在任何服务创建之前都会过早运行,换句话说,提供程序的 $get 是服务构造函数,并且在模块的配置阶段之后实例化(当首次通过依赖注入访问它时,注入器将实例化构造函数并将其保留为单例)。提供程序在配置阶段期间或之前运行(因为提供程序方法用于在模块的 config 阶段期间配置服务)。这意味着 $log 服务尚不可用。

$logProvider.$get 将为您提供 logservice 的构造函数,您可以通过调用 $injector.instantiate($logProvider.$get) 自己创建一个实例,但问题是它依赖于尚未实例化的 window 服务,因此最终您的记录器实例化将失败。

因此,我所能考虑的一种方法就是从另一个注入器中获取 $log,即 angular.injector(['ng']).get('$log')

也就是说

angular.module('my.module', [])
  .provider('myProvider', function ($log, $logProvider) {
    var $log =  angular.injector(['ng']).get('$log');
    $log.log("Aloha!"); 

    this.$get = function($log) {
        $log.log("Hello!"); // Everything is ok here
    };
});

Plnkr

或者另一种方法就是自己疯狂实例化它,实例化它的依赖服务,在这种情况下只需要$window(甚至可以提供全局窗口对象作为局部变量)。

 .provider('myProvider', function ($logProvider,$injector, $windowProvider) {
    //get window service, if you want to really provide window service instance itself, or just provide the global window object
    var window = $injector.instantiate($windowProvider.$get);
    //Get log provider providing locals for $window
    var $log =  $injector.instantiate($logProvider.$get,{$window:window})

    $log.log("Aloha!");// Everything is ok here too

    this.$get = function($log) {
        $log.log("Hello!"); // Everything is ok here
    };

});

Plnkr

另外要注意的一点是:在应用程序的配置阶段尝试访问$log服务时,您将看到相同的行为。但有时由于装饰器的工作方式,您仍然可以通过使用虚拟装饰器来强制提前创建服务并利用它。

例如:

.config(function($provide){
   //Just a dummy decorator
   $provide.decorator('$log', function($delegate){
      return $delegate;
  });

}).config(function($logProvider){
   //get logger instance
   var log = $logProvider.$get();
   log.debug("Got it");
});

Plnkr

最终的想法是,当你需要在服务实例化之前使用它时,你需要手动解析所有依赖项等来实例化它。


只是补充一下,这个$log将是一个不同的实例,而不是注入到应用程序中的实例。对于$log可能没有关系,但如果是另一个服务,那就有可能了。 - New Dev
@PSL,“angular.injector(['ng']).get('$log')”是一个不错的解决方案!(我们仍然会失去自定义装饰器的逻辑,但我已经接受了这个悲伤的事实)。但你没有回答我的问题。我认为用户应该能够在提供程序和配置部分中使用$log(或其他内容)。你认为这个问题没有“真正的Angular”解决方案吗?是否有类似于本机Angular.log()方法的东西。 - Harry Burns
@harryburns 坦白地说,在这些块内部,我从来没有做过太多的日志记录。如果需要,只需使用console.log..无论如何,您不能真正通过注入$log服务本身来利用它,那有什么意义呢..而且在angular中也没有静态日志函数可用。 - PSL
@PSL,好的。最后一次机会,我将停留在使用自己的logProvider解决方案上,该解决方案可以从配置部分和其他提供程序中访问。这比直接使用console.log更清晰。感谢您的帮助! - Harry Burns
哇,这么简单的事情却有这么多的词汇。Angular 有时真是一个可怕的世界。 - jediz

0

你可以编写一个包装器服务,比如说日志记录器,它可以有像log、warn、debug等方法,并在需要的地方注入你的日志记录器服务,你可以在那里进行格式化。

例如: 下面的方法在我的日志记录器服务中,只需注入日志记录器并调用log方法即可。

this.log = function () {
        if (this.debuggingEnabled) {
           for (var i = 1; i < arguments.length; i++) {
              $log.log("[" + $filter('date')(new Date(), this.format) + "] -- " + arguments[0] + " -- " + JSON.stringify(arguments[i]));
           }
        }
     };

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接