I wanted to add an interceptor to my module.
Here is the intial state:
app.config(function ($stateProvider, $urlRouterProvider,RestangularProvider) {
RestangularProvider.setBaseUrl('/app/services');
});
I modified with this:
app.config(function ($stateProvider, $urlRouterProvider, RestangularProvider, RestangularConfigurer) {
RestangularProvider.setBaseUrl('/app/services');
RestangularConfigurer.addFullRequestInterceptor(function (element, operation, route, url, headers, params, httpConfig) {
if (operation === 'get'){
console.log("RestangularProvider: call to get");
params.ts= new Date();
}
return {
element: element,
headers: headers,
params: params,
httpConfig: httpConfig
};
});
});
However, I add an error:
Error: [$injector:unpr] Unknown provider: RestangularConfigurer
So I replaced RestangularConfigurer by RestangularProvider.
But I do not clearly understand how that works, and what's happening behind the scenes.
Could you explain me that ?
First of all there is no RestangularConfigurer module. You have only Restangular and its provider RestangularProvider.
Let's start with angularjs documentation for providers.
Please esspecially read Provider Recipe as I do not duplicate it again here.
As a summary RestangularProvider is just a recipe how Restangular api should be build as you decide it in config module there is no Restangular api yet, but you have your recipe.
On the other hand Restangular will be bootstraped after your main module bootstraped, so in config there is actually no Restangular module yet.
If you still insist using Restangular instead its provider you can use it in module#run instead of module#config.
Related
am getting an error
Error: Unexpected request: POST data/employee/1
No more request expected
Am using angular-mocks, angular-ui-route in my app.config i've got my $stateProvider routes and my mocks the main one with the issue being the one below:
$httpBackend.whenPOST(/data\/employee\/(\d+)/, {}, {},['id']).respond(function(method, url, data, headers, params){
console.log('Received these data:', method, url, data, headers, params);
return [200, {}, {}]
});
Calling via controller:
app.controller("employeeController", ['$scope','$http', "$state", "$stateParams", function($scope, $http, $state, $stateParams){
$http.post("data/employee/1").then(function(response){
})
})
What might be the issue?
In order for the mock $httpBackend.whenPOST(url, [data], [headers], [keys]) to work optional parameters data and header need to be declared as undefined where they are not used and not just as empty objects {}.
Firstly, apology if this question does not make sense. I am developing code for session management for my mean stack app. From last few days, i found lots of way to implement it which are using either cookies, sessions or http - headers. I tried to implement, but did not get success.
I successfully link the interceptor with my code. Code is listening to each req/res.
Here is some code:
app.js
angular.module('MyApp', [
'ngMaterial',
'ngMdIcons',
'ui.router',
'e3-core-ui.services',
'e3-core-ui.utils'
])
.config(['$stateProvider', '$routeProvider','$httpProvider','$mdThemingProvider', '$mdIconProvider', function($stateProvider, $routeProvider, $httpProvider, $mdThemingProvider, $mdIconProvider) {
$httpProvider.interceptors.push('YourHttpInterceptor');
...
Interceptor-code
angular.module('MyApp')
.factory('YourHttpInterceptor', ['$q',
function($q, ) {
return {
'request': function(config) {
console.log("req");
return config;
},
// Optional method
'response': function(response) {
// do something on response success
console.log("inside the response ");
return response;
},
// optional method
'responseError': function(rejection) {
// Here you can do something in response error, like handle errors, present error messages etc.
console.log("inside the response error ");
return $q.reject(rejection);
}
};
}]);
I will be very thankful for your time and help.
In Meanjs you have the authentication controller
mean/modules/users/client/controllers/authentication.client.controller.js
but if you want to use the authentication service in your interceptor, just be aware that injecting a dependency isn't that easy as doing it in a controller.
you'll need to use $injector
var AuthService = $injector.get('Auth');
then you'll have to be sure your user is authenticated and check that in your request function, something like
if (!Authentication.user) {
$location.path('/'); // or a login page
}
I'm very new to Angular, so it's perfectly possible I made some stupid mistakes.
What I'm trying to do is use a token that is saved in localStorage and put that in all requests.
I have this code block:
.config(['$httpProvider', 'localStorageService', function($httpProvider, localStorageService) {
//Http Interceptor to check auth failures for xhr requests
$httpProvider.interceptors.push('authHttpResponseInterceptor');
$httpProvider.defaults.useXDomain = true;
$httpProvider.defaults.headers.common.Authorization = 'Token token=' + localStorageService.get("TemponiaToken")
}])
And I'm getting this error message:
Uncaught Error: [$injector:modulerr] Failed to instantiate module starter due to:
Error: [$injector:unpr] Unknown provider: localStorageService
I found this question and answer which (I think) explains the problem: Why can't I use the localStorage in my .config when it's include in the .module definition?
However: how can I solve this? Change the .config into .run ? Thanks for any pointers in the right direction.
As the answer you found says is not possible to inject services during the config phase.
There are two possible solution to your problem.
The first one is to use the native localStorage object
localStorage.getItem("TemponiaToken") // it just works without injecting any service
The other one is to correctly define the interceptor
yourApp.factory('AuthInterceptor', function (localStorageService) {
return {
request: function (config) {
config.headers = config.headers || {};
config.headers.Authorization = 'Token token=' + localStorageService.get("TemponiaToken")
return config;
}
};
});
yourApp.config(function ($httpProvider) {
$httpProvider.interceptors.push('AuthInterceptor');
});
The documentation of ngStorage says thus:
Usage from config phase
To read and set values during the Angular config phase use the .get/.set functions provided by the provider.
var app = angular.module('app', ['ngStorage'])
.config(['$localStorageProvider',
function ($localStorageProvider) {
$localStorageProvider.get('MyKey');
$localStorageProvider.set('MyKey', { k: 'value' });
}]);
It seems this is working solution that shows how to work with $httpBacked http://jsfiddle.net/EgMpe/8/
But for my case:
routes
app.config(['$routeProvider', function($routeProvider) { $routeProvider.
when('/', {templateUrl: 'partials/user-list.html'}).
...
faked service:
app.run(function($httpBackend) {
var users = [{"id":1,"name":"bob","email":"bob#bobs.com"}, {"id":2,"name":"bob2","email":"bob2#bobs.com"}]
$httpBackend.whenGET('/rest/users').respond(function(method,url,data) {
console.log("Getting users");
return [200, users, {}];
});
});
..
real service:
services.factory('Users', function($resource){
return $resource('/rest/users', {}, {
get: {method: 'GET', isArray:true}
});
});
I have error when go to my "/" route that redirects me to user-list.html page:
Error: Unexpected request: GET partials/user-list.html No more request
expected
at $httpBackend .../mysite/public/angular/libs/angular-1.2.0/angular-mocks.js:1060:9)
Question1: Does httpBackend prevent doing any other http request?
I tried to use passThrough method to let http hit real server side:
$httpBackend.whenGET(/^\/mysite\//).passThrough();
But this does not help.
Using $httpBackend you have to specify in advance all request you are going to perform. Maybe this short excerpt from Mastering Web Application Development with AngularJS will clarify why:
The verifyNoOutstandingExpectation method verifies that all the expected
calls were made ($http methods invoked and responses flushed), while the
verifyNoOutstandingRequest call makes sure that code under test didn't trigger
any unexpected XHR calls. Using those two methods we can make sure that the code
under the test invokes all the expected methods and only the expected ones.
Ah.. Sorry I just was wrong with my RegEx:
if type this $httpBackend.whenGET(/partials/).passThrough();
Then all start working.
So, I got my lesson: don't forget to put: passThrough(); with right RegEx.
Now I know that because of the way javascript executes it is recommended that you run all remote requests as async instead of sync. While I agree with that 99% of the time, sometimes you do want to run remote request as a sync instead of a async. For example, loading session data is something I would want to do synchronically as I don't want any views to render until that data is loaded. This plunker shows the issue with loading session data asynchronically (NOTE: I am using $timeout to simulate what would happen with an async call):
http://plnkr.co/edit/bzE1XP23MkE5YKWxRYrn?p=preview
The data property does not load anything because the data is not available when it tries to get it and data2 does only because the data is available when it tries to get it. Now in this case I could just put the session variable on the scope and be done with it but that is not always the case.
Is there a better way to do sync remote calls in an angular application other than using jQuery's .ajax() method (trying to depend on jQuery as little as possible)?
If you want the session data to be loaded prior to a controller being loaded, you should included it as as resolve parameter (assuming you are using the $routeProvider).
For example:
angular.module('mymodule', ['ngResource'])
/* here's our session resource. we can call Session.get() to retrieve it. */
.factory('Session', ['$resource', function($resource) {
return $resource('/api/session.json');
}])
/* here's our controller + route definition. */
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/foo', {
controller: 'MyCtrl',
templateUrl: '/templates/foo.html',
/* the controller will not be loaded until the items
* below are all resolved! */
resolve: {
session: ['$q', 'Session', function($q, Session) {
var d = $q.defer();
Session.get(function(session) {
/* session returned successfully */
d.resolve(session);
}, function(err) {
/* session failed to load */
d.reject(err);
});
return d.promise;
}]
}
});
}])
.controller('MyCtrl', ['$scope', 'session', function($scope, session) {
/* 'session' here is the key we passed to resolve above.
* It will already be loaded and resolved before this function is called */
$scope.session = session;
}]);
Angular is hardcoded to make the requests async. To do it synchronously would take other code, whether custom or from some other library. Here is line 9269 from angular 1.0.7:
xhr.open(method, url, true);
The third param makes it asynchronous.
I would take a step back and think about how you are doing things. You could provide some loading indicator while your async request is going and easily control the loading of a view in the success callback so that it doesn't appear until the data is loaded.
A better solution is to add a response interceptor:
checkAuth = ($q, $location) ->
success = (response) ->
response
error = (response) ->
errorCode = response.status
$location.path '/login' if errorCode is 403 or errorCode is 401
# $q.reject response - no need because we are redirecting before any other promises in the chain will resolve (were breaking our future promises)
(promise) ->
promise.then success, error
$httpProvider.responseInterceptors.push checkAuth
And in your $routeProvider, or $stateProvider in my case:
.state 'user',
templateUrl: 'assets/views/user/partials/user.html'
resolve:
credentials: (checkLogIn) ->
checkLogIn.get().$promise
When checkLogIn.get()'s promise is rejected (the error handler is fired), assuming it's a 401 or 403 response (unauthenticated or unauthorized), the promise chain will be broken and the user will be "redirected" to /login.
With this method, any error calls will be channelled into the interceptor, instead of handling errors on a route-by-route basis.