Use $Http Inside Custom Provider in App Config, Angular.Js

use $http inside custom provider in app config, angular.js

The bottom line is:

  • You CANNOT inject a service into the provider configuration section.
  • You CAN inject a service into the section which initializes the provider's service.

Details:

Angular framework has a 2 phase initialization process:

PHASE 1: Config

During the config phase all of the providers are initialized and all of the config sections are executed. The config sections may contain code which configures the provider objects and therefore they can be injected with provider objects.
However, since the providers are the factories for the service objects and at this stage the providers are not fully initialized/configured -> you cannot ask the provider to create a service for you at this stage -> at the configuration stage you cannot use/inject services.
When this phase is completed all of the providers are ready (no more provider configuration can be done after the configuration phase is completed).

PHASE 2: Run

During run phase all the run sections are executed. At this stage the providers are ready and can create services -> during run phase you can use/inject services.

Examples:

1. Injecting the $http service to the provider initialization function WILL NOT work

//ERRONEOUS
angular.module('myModule').provider('myProvider', function($http) {
// SECTION 1: code to initialize/configure the PROVIDER goes here (executed during `config` phase)
...

this.$get = function() {
// code to initialize/configure the SERVICE goes here (executed during `run` stage)

return myService;
};
});

Since we are trying to inject the $http service into a function which is executed during the config phase we will get an error:

Uncaught Error: Unknown provider: $http from services 

What this error is actually saying is that the $httpProvider which is used to create the $http service is not ready yet (since we are still in the config phase).

2. Injecting the $http service to the service initialization function WILL work:

//OK
angular.module('myModule').provider('myProvider', function() {
// SECTION 1: code to initialize/configure the PROVIDER goes here (executed during `config` phase)
...

this.$get = function($http) {
// code to initialize/configure the SERVICE goes here (executed during `run` stage)

return myService;
};
});

Since we are now injecting the service into the service initialization function, which is executed during run phase this code will work.

Use ownProvider with $http call inside config

I finished with conclusion that it is not possible to use gathered info from backend call in config(). Maybe it is possible to use some kind of hack with resolve function but I couldn't figure it out.

What I did instead of this call is I used window.navigator.language in my config() so now my translation script is not based on user IP but it's based on user browser language. It's not as satisfying as IP because I still can have user which is from Poland and has browser set to english and then my page will display in english.

I could also write own service which would translate page for me but I really wanted to use angular-translate module: link to module.

Note that I used window.navigator.language not a $window because this second one is not achievable in config() as well. So my solution may cause some problems during testing.

I wrote this for anyone who will came into this topic in future.

angularjs custom module with $http injection

with this code it works

var initInjector = angular.injector(['ng']);
var $http = initInjector.get('$http');

How can I directly inject $http into a provider?

You can never inject service instances into config functions or providers, since they aren't configured yet. Providers exist to configure specific services before they get injected. Which means, there's always a corresponding provider to a certain service. Just to clarify, here's a little example configuring $location service using $locationProvider:

angular.module('myModule').config(function ($locationProvider) {
$locationProvider.html5Mode(true);
});

So what happens here, is that we configure $location service to use its html5mode. We do that by using the interfaces provided by $locationProvider. At the time when config() is executed, there isn't any service instance available yet, but you have a chance to configure any service before they get instantiated.

Later at runtime (the earliest moment ist the run() function) you can inject a service. What you get when injecting a service is what its providers $get() method returns. Which also means, each provider has to have a $get() function otherwise $injector would throw an error.

But what happens, when creating custom services without building a provider? So something like:

angular.module('myModule').factory('myService', function () {
...
});

You just don't have to care about, because angular does it for you. Everytime you register any kind of service (unless it is not a provider), angular will set up a provider with a $get() method for you, so $injector is able to instantiate later.

So how to solve your problem. How to make asynchronous calls using $http service when actually being in configuration phrase? The answer: you can't.

What you can do, is run the $http call as soon as your service gets instantiated. Because at the time when your service get instantiated, you're able to inject other services (like you always do). So you actually would do something like this:

angular.module('myModule').provider('custom', function (otherProvider, otherProvider2) {
// some configuration stuff and interfaces for the outside world

return {
$get: function ($http, injectable2, injectable3) {
$http.get(/*...*/);
}
};
});

Now your custom provider returns a service instance that has $http as dependency. Once your service gets injected, all its dependencies get injected too, which means within $get you have access to $http service. Next you just make the call you need.

To make your this call is getting invoked as soon as possible, you have to inject your custom service at run() phrase, which looks like this:

angular.module('myModule').run(function (custom, injectable2) {
/* custom gets instantiated, so its $http call gets invoked */
});

Hope this makes things clear.

Inject service in app.config

Alex provided the correct reason for not being able to do what you're trying to do, so +1. But you are encountering this issue because you're not quite using resolves how they're designed.

resolve takes either the string of a service or a function returning a value to be injected. Since you're doing the latter, you need to pass in an actual function:

resolve: {
data: function (dbService) {
return dbService.getData();
}
}

When the framework goes to resolve data, it will inject the dbService into the function so you can freely use it. You don't need to inject into the config block at all to accomplish this.

Bon appetit!

$provide outside config blocks

After some Angular injector study I was able to give an exhaustive answer to my own question.

Essentially, $injector in config blocks and provider constructor functions and $injector everywhere else are two different services with the same name, which are defined on internal provider/instance cache explicitly, together with $provide (this one is being defined in provider cache, hence it can be injected in config only).

While generally not recommended because of probable race conditions, it is possible to expose internal services to instance cache and make config-specific $provide and $injector available for injection after config phase has ended:

app.config(function ($provide, $injector) {
$provide.value('$providerInjector', $injector);
$provide.value('$provide', $provide);
});

The possible applications are configuring service providers any time (if possible)

app.run(function ($providerInjector) {
var $compileProvider = $providerInjector.get('$compileProvider');
...
});

and defining new components at run-time

app.run(function ($provide) {
$provide.controller(...);
...
});


Related Topics



Leave a reply



Submit