I am trying to refactor my AngularJS application and introduce a login service. Currently my controller has methods like login(), logout(), getCurrentUser(), which make http requests and handle the user object. I want to move these functions into a service, because I need to call the getCurrentUser() method from multiple controllers.
I have written the following service:
angular.module('common.login', []).
factory('loginService', function ($http) {
return {
user: null,
error: null
};
});
So at the moment the service is not doing anything. However I get an error:
Uncaught Error: Circular dependency: loginService <- $http
If I remove the $http dependency from the callback function, then the service works. I have trouble understanding why there is a circular dependency. The loginService factory is injected into two places: A **main module's config callback ** and a **login module's controller callback **. The config callback configures a http interceptor, the login controller also uses the http services.
The circular dependency is caused because you configure an httpInterceptor either to send some auth token or to intercept 401 error that depend on your login service that depend itself from the $http service.
Your httpInterceptor should not depend of your login service.
For instance if you need to send a token, you could store it in localStorage when you login and then in your httpInterceptor read it from there instead of your loginService.
If you need to logout the user when you get a 401 error then juste redirect the user to /logout instead of calling a logout method on your loginService.
Related
My apps are using many web services on the intranet, and url-s for those depend on the server environment.
My apps are hosted on IIS, which adds an HTTP response header like this: Environment: DEV, so every web app knows in which server environment it is running, and thus which intranet servers it must use to call all the services.
Each of my angular apps uses a service that issues a simple GET against the app's own root just to get any response with the environment name in it, and set configuration accordingly.
Question:
How should an angular app implement such a service that would execute as the very first thing in the application, and make sure that while it is getting that first response, nothing in the app tries to execute an HTTP request against other services, or even try to use any configuration provided by my environment service?
Is there a way to implement such a service in angular that could block every other service / factory in the application till it is done initializing itself?
I have many other services in the app, and none of them really know what to do till my environment service has finished its initialization.
UPDATE
Looking at it from another angle.... is it possible to implement such an interceptor in angular that could do the following?:
execute an HTTP request and block the app's execution till it gets a response
make information from the response available throughout the app as a service/factory/config.
Angular lifecycle could be one solution. Using the angular.config() phase you could peek at the headers of the HTTP service.
Create a factory called 'httpInterceptor'
function httpInterceptors(siteConfig, $q, $injector) {
return {
response: function(data, status, headers) {
siteConfig.setEnvironment(headers['Environment']);
return data;
}
};
)
Then in angular.config()
$httpProvider.interceptors.push('httpInterceptor');
If you truly want to block the other option is to use UI router resolve property to block routes loading until the request has been made https://github.com/angular-ui/ui-router/wiki you can add the resolve method to the root state.
Resolve
You can use resolve to provide your controller with content or data that > is custom to the state. resolve is an optional map of dependencies which > should be injected into the controller.
If any of these dependencies are promises, they will be resolved and converted to a value before the controller is instantiated and the $stateChangeSuccess event is fired.
I am implementing an angularjs service, which saves the data sent by an $http call in localStorage. In order to do that, I am using the request interceptor, so that whenever an http request is sent via $http, the data is saved in localStorage. Below is my code for the interceptor,
var OfflinkJs = angular.module('OfflinkJs', []);
OfflinkJs.factory('cacheInterceptor', function () {
var cacheInterceptor = {
request: function (config) {
// Here I am saving the config as a string in localstorage
return config;
}
};
return cacheInterceptor;
});
For above interceptor to work, I have to register it in the interceptors array of $httpProvider. I have done this to achieve that,
OfflinkJs.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push('cacheInterceptor');
}]);
PROBLEM
Now, when I use OfflinkJS module in another module, all the $http calls go through my interceptor. But I would like to make some requests sent by $http service use my interceptor while some other requests NOT.
Since $http service is a singleton, I cannot figure out how to use two instances of it in separate places of my application. Is there any way to achieve this?
I went through this question, but seems it really addresses the issue of Circular dependency
I need two instances of AngularJS $http service or what?
I check the URL in the interceptor and use that to filter out requests to other services. I set the base url for my service as a constant in my module, and then check against that. If the request isn't to the relevant service, it just passes through with no action.
But perhaps a better way would be to set up a data service instead of an interceptor. There are plenty of tutorials out there on data services.
Suppose I have an angular app called chococalateApp that depends on 3 other modules, namely Product, Sales and LogIn.
Now, my app is building on RESTful API. Upon successful login, the server will respond by sending back an authentication token. I would like to append this token as a custom header X-AUTH whenever $http services are used. Since all my REST API requires the auth token, I would need to append this header in every $http request. This is doable by configuring the $httpProvider, as shown below:
angular.module('chocolateApp',['Product','Sales','Login'])
.config(['$httpProvider', function($httpProvider){
$httpProvider.defaults.headers.common['X-AUTH'] = 'randomkeybyserver'
}
])
My question is, can I inject the value of the auth-token AFTER the module has been bootstrapped?
For example, I have a service in LogIn module that is able to do the authentication, and retrieved the required token. How do I pass the token back to my main chocolateApp module and configure it? Will this result in circular dependency, or is it that my understanding of DI is wrong here?
If this is not achievable, how should this be designed?
You can do it from your Login service after authentication and it will be available across your app.
login: function(...) {
$http.post(...).then(function(response) {
$http.defaults.headers.common['X-AUTH'] = response.data.randomkeybyserver;
});
}
I would like to know whether we can create a provider in angularjs which will replace the $http operation .which means where we can use this provider in other modules where we can make use of these $http operation.
The reason why provider has to be taken is because we can configure the http parameters like the api path, request type .
Also can we have logging/exception handling mechanism inside the provider so that the modules(eg: any other factories) which inherit the provider wont need to do any extra logging/exception mechanisms.
Is there any way to have some loading screen using this provider when http requests are made ?
For the things you mentioned, you don't need another provider, because $http has the concept of interceptors.
Interceptors can specify different callbacks to be executed at different phases:
request (runs before any request is sent): It can modify the configuration (e.g. the request URL, method etc). It could also be used to show some loading message/animation (e.g. using some property on the $rootScope).
requestError (runs when there is an error before sending the request): It can be used for logging, recovering, exception handling.
response (runs after any response is received): It can be used for logging. It could also be used to hide the loading message/animation. (Don't forget to also handle this on response error.)
responseError (runs when there is an error regarding the response (e.g. bad request)): It can be used for logging, recovering, exception handling.
If interceptors do not cover your needs, you could use $provide's decorator to monkey-patch, augment or totally replace the $http service:
.config(function ($provide) {
$provide.decorator('$http', function ($delegate) {
var newHttp = $delegate; // or a totally new object
// ...monkey-patch newHttp or define new methods or whatever
return newHttp;
});
});
I'm searching a solution but it's always the same and she's not correpond to my problem.
I have a lot of route element but for all routes with different controller I need to know if the user is authenticate or not. If yes, I have a token from an api and if no, I have a token too. So I need to have a token from an api when I load all controllers.
So I don't want to make a resolve for each "when()", I don't want to ddos the API so I search a system like this with
app.run(['Auth', function(Auth){
// Call my service & waiting his resolve
// When resolved, continue the init
}]);
So how can I make a resolve system with my "Auth" service on the run() ?
Make a service where you will have variable with status of authentication, inject this service in all controllers and in each controller call method of that service. Inside that method check variable with authentication status and only if authentication request was not yet send - send it (and set variable to status sent).