2

In my app I need to inject "dateFilter" in the config block. I know I can't do it like this:

.config(function(dateFilter){})

Since dateFilter is not a provider or a constant, it's not available during config.

However, after some research, I made it work by using the following in the config:

angular.injector(["ng"]).get('dateFilter')('2014-01-01','yyyy/MM/dd');

Doesn't this mean that I can get anything during config? Then what's the point making only providers and constants injectable during config? Is it bad to do something like angular.injector(["ng"]).get('dateFilter') during config?

Shawn
  • 2,675
  • 3
  • 25
  • 48
  • Rarely need to do this and chances are you don't need to either but you haven't shown or explained why you need it directly in config. Note that you can inject locals into routing resolves if that has anything to do with use case – charlietfl Jun 12 '16 at 18:25
  • http://stackoverflow.com/questions/32566416/change-format-of-md-datepicker-in-angular-material The answer here is what I want to do. I don't use moment.j s so I use dateFilter instead. – Shawn Jun 12 '16 at 18:49
  • Can do that in `run()` block instead where you can inject locals – charlietfl Jun 12 '16 at 18:51
  • `$mdDateLocaleProvider` isn't available at `run` block. – Shawn Jun 12 '16 at 19:28
  • Wow it works. It's not in the docs. How do you know? – Shawn Jun 12 '16 at 19:45

2 Answers2

1

angular.injector shouldn't be used in production, unless the circumstances are really exotic (i.e. almost never). It creates a new injector instance and introduces some overhead. Conventional Angular DI is good for its testability, while angular.injector turns a part of the application into untestable piece of code. Always reuse current injector inside the app, if possible (i.e. almost always).

Usually 'how to use service instance in config block' type of questions indicates an XY problem. The fact that Angular uses config to configure service providers that thereafter will create service instances (chicken-egg dilemma) suggests that the application should be refactored to respect Angular life cycle.

However, built-in filters are stateless helper functions, and their use in config phase is relatively harmless. dateFilter service is defined by $filterProvider, and $filterProvider should be injected to get to dateFilterProvider. The problem is that dateFilter depends on $locale service, which wasn't instantiated yet. $locale is constant (in broad sense) that doesn't depend on other services, so it has to be instantiated too.

angular.module('...', [
  'ngLocale' // if used, should be loaded in this module
])
.config(($filterProvider, $localeProvider, $provide, $injector) => {
  var $locale = $injector.invoke($localeProvider.$get);
  var dateFilterProvider = $injector.get('dateFilterProvider')
  var dateFilter =  $injector.invoke(dateFilterProvider.$get, { $locale: $locale });

  $provide.constant('dateHelper', dateFilter);
})

This is a hack should be taken into account in tests (dateHelper service should superficially tested) but is relatively trouble-free and idiomatic.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • So `angular.injector` creates a new instance, but `$injector` reuses the current instance? So it's fine to use `$injector` as in your example? – Shawn Jun 13 '16 at 00:30
  • Yes. Notice that you can't do $injector.get() to get service instance in config, so $injector.invoke should be invoked with $get function, this does the trick. – Estus Flask Jun 13 '16 at 00:47
  • Do I need this line `$provide.constant('dateHelper', dateFilter);`? I can just use `dateFilter` after `var dateFilter = $injector.invoke(dateFilterProvider.$get, { $locale: $locale });`, right? – Shawn Jun 13 '16 at 00:52
  • Not really, but this will improve testability, and tests are important here (if Angular will change the way $filter deals with built-in filters, the app will be screwed with no explanation). Again, this may be XY problem, and you may not need to do this in `config`. – Estus Flask Jun 13 '16 at 10:04
0

you cant inject services in config only provides but you can do it in app.run here's the calling order:

  1. app.config() //only provides can be injected
  2. app.run() //services can be injected
  3. directive's compile functions (if they are found in the dom)
  4. app.controller()
  5. directive's link functions (again, if found)
avim101
  • 300
  • 1
  • 2
  • 9