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
Setting Multiple Attributes for an Element at Once with JavaScript
How to Remove Spaces from a String Using JavaScript
How to Animate Scrolltop with Jquery
JavaScript Use Variable as Object Name
Jquery Function to Get All Unique Elements from an Array
Is There a Sleep Function in JavaScript
Handle Url Fragment Identifier (Anchor) Change Event in JavaScript
How to Pass Parameters to a Script Tag
Validate That a String Is a Positive Integer
JavaScript - Difference Between Array and Array-Like Object
Plain Count Up Timer in JavaScript
Download File from Bytes in JavaScript
How to Define Setter/Getter on Prototype
Sum of Two Numbers with Prompt
How to Implement Prepend and Append with Regular JavaScript