Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS Provider dependency injection - using $log in provider

The problem is simple: using AngularJS we can't inject $log into provider.

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
        };
    });

Yes, we can inject $logProvider, but it has no needed methods(.log, .error, etc.).

Yes, we can inject $logProvider, and then manually call $logProvider.$get(), but we will unable to use additional logic from decorators.

Yes, we can write our own logProvider, but I wonder why Angular don't support this feature out-of-the-box.

So, we can't use console in 'true angular way' in provider? That fact is very strange. And sad.

The question: how I need to use console in "true Angular way" in providers?

I was unable to find any explanations of this problem. Angular Developers Guide says that we need to use $log everywhere instead of console.

like image 562
Harry Burns Avatar asked Feb 20 '15 02:02

Harry Burns


People also ask

Does AngularJS support Dependency Injection?

Dependency Injection is pervasive throughout AngularJS. You can use it when defining components or when providing run and config blocks for a module.

How can you configure the injector to use an existing object for a token?

Using an InjectionToken objectlink content_copy import { InjectionToken } from '@angular/core'; export const APP_CONFIG = new InjectionToken<AppConfig>('app. config'); The optional type parameter, <AppConfig> , and the token description, app. config , specify the token's purpose.

How do I inject a service in AngularJS?

There is one more way to inject dependencies in AngularJS: by using the $inject service. In doing so, we manually inject the dependencies. We can inject $scope object dependencies using the $inject service as shown in the listing below: function ProductController($scope){ $scope.

Which provider is used for dynamic Dependency Injection?

The useFactory provider key lets you create a dependency object by calling a factory function, as in the following example. The injector provides the dependency value by invoking a factory function, that you provide as the value of the useFactory key.


1 Answers

Providers run too early before any services are created or in other words provider's $get is the service constructor and it gets instantiated after the config phase of the module (and when it is accessed for the first time via dependency injection the injector instantiates the constructor and keep it as singleton). And providers run during or before the config phase (since the provider methods are used for configuring the service during the config phase of the module). Which means $log service is not available yet.

$logProvider.$get will give you the constructor for logservice, you could yourself create an instance of it by calling $injector.instantiate($logProvider.$get) but the problem is that it has a dependency on window service which has not bee instantiated yet, so ultimately your logger instantiation will fail.

So one way i could just think of is to get $log from another injector, i.e angular.injector(['ng']).get('$log').

i.e

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

Or another way just go crazy instantiate it on your own, instantiating its dependent services in this case it is just the $window (or even provide global window object as locals).

 .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

Just to add a different note: You will see the same behavior while trying to access the $log service during the config phase of the app as well. But sometimes due to the way decorators work you could still make use of it by forcing an early service creation by using a dummy decorator.

i.e:

.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

So ultimately the idea is that, when you need to use a service before it has been instantiated yet, you would need to manually instantiate it by resolving all of its dependencies and so on.

like image 152
PSL Avatar answered Sep 20 '22 17:09

PSL