AngularJs, $resource: How to read expanded URL template? - angularjs

Is there a way to read URL template expanded with all the parameters (URL which was really called) after call to server in AngularJS $resource?
It is for logging purpose when something goes wrong with the call I would like to log the actual URL.

You could accomplish that with an interceptor. This way you dont have to add your logging stuff to all the $resources'.
This goes in your app.js
// register the interceptor as a service
$provide.factory('loggingInterceptor', function($q) {
return {
// optional method
'request': function(config) {
return config;
},
// optional method
'requestError': function(rejection) {
// log your error
return $q.reject(rejection);
},
// optional method
'response': function(response) {
// do something on success
return response;
},
// optional method
'responseError': function(rejection) {
// log your error
return $q.reject(rejection);
}};
});
$httpProvider.interceptors.push('loggingInterceptor');
Btw, you can get the url from the 'config object in the request method. Just do a console.log(config) and pick it up from there. I think its config.url
More on interceptors in AgularJS $http service documentation under the Interceptors topic.

Related

How to add add request parameter to every Angular.js $http request (to start a xdebug session for example)

My hybrid application is based on AngularJS and uses a php REST api.
I would like to debug the php api directly from my Angular app instead to use REST console or Postman. It would save a lot of time especially for POST and PUT requests.
In order to do so I would need to add a parameter to each request like so:
http://localhost:8000/api/contacts?XDEBUG_SESSION_START=PHPSTORM
Can I config $http to do so?
You can use httpInterceptor for that (official $http documentation contains more info)
// register the interceptor as a service
$provide.factory('xdebugInterceptor', function($q) {
return {
// optional method
'request': function(config) {
// do something on success
// !!! adjust the config object
// add request param XDEBUG_SESSION_START=PHPSTORM
// it will be added to every made request
config.params = config.params || {};
config.params.XDEBUG_SESSION_START: "PHPSTORM";
return config;
},
// optional method
'requestError': function(rejection) {
// do something on error
return $q.reject(rejection);
},
// optional method
'response': function(response) {
// do something on success
return response;
},
// optional method
'responseError': function(rejection) {
// do something on error
return $q.reject(rejection);
}
};
});
// make this conditional so you use it only in DEV mode
$httpProvider.interceptors.push('xdebugInterceptor');

In angular, how can I catch the moment when an ajax request is successfully sent?

With angular $resource, I would like to fire a callback function when a request is successfully sent to the restful backend. (The backend may take a long time and I only want to know if it received the data I sent.)
The only thing I've found so far is resource.action.$promise['finally'](callback);
I'd be also interested to know when the request could not be sent. (eg. connection problems)
Thanks!
Here is DRY approach :
Build a service intercepting every HTTP requests (like the one defined in the official documentation) :
$provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
return {
// optional method
'request': function(config) {
// do something on success
return config;
},
// optional method
'requestError': function(rejection) {
// do something on error
if (canRecover(rejection)) {
return responseOrNewPromise
}
return $q.reject(rejection);
},
// optional method
'response': function(response) {
// do something on success
return response;
},
// optional method
'responseError': function(rejection) {
// do something on error
if (canRecover(rejection)) {
return responseOrNewPromise
}
return $q.reject(rejection);
}
};
});
Simply put your code inside the desired hooks. You could for instance draw an error modal dialog if the request fails.
Finally, Register it to your application :
app.config(['$httpProvider', function($httpProvider) {
// Add the interceptor to the $httpProvider to intercept http calls
$httpProvider.interceptors.push('myHttpInterceptor');
}]);

AngularJS $resource interceptors

How do I add interceptors to a $resource call?
Let's say I have a resource factory called Users, like so;
app.factory('Users', ['$resource', 'resourceInterceptor',
function ($resource, resourceInterceptor) {
return $resource(
'users/:user_id',
{
user_id: '#id'
},
{
query: {
method: 'GET', // Not changing the default method, just adding an interceptor
interceptor: resourceInterceptor // Is there any better way to do this.. like globally?
},
save: {
method: 'POST', // Same here
interceptor: resourceInterceptor // Again...
},
..., // And so on
}
);
}]);
and my resourceInterceptor service looks like;
app.factory('resourceInterceptor', ['$rootScope',
function ($rootScope) {
return {
request: function () {
// This function isn't executed at all?
$rootScope.loading = true;
},
response: function () {
$rootScope.loading = false;
},
responseError: function () {
$rootScope.loading = false;
}
};
}]);
First of all, the request intercept function is never executed, why not?
Secondly, having to hardcode the interceptor to existing $resource methods is very tedious , is there a way to easier assign interceptors to specific $resource calls, or maybe even assign an interceptor to all $resource calls?
To use an interceptor in a resource you should:
1 - Make an httpInterceptor with you request, response, responseError:
app.factory('myInterceptor', function () {
//Code
//return { request:...,
});
2 - Config this Interceptor in your app:
app.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push('myInterceptor');
}]);
Right now as you have config your httpProvider to has an interceptor wherever you inject $http you will use this provider so... you will excute your request, response and responseError funciton.
3 - Using it in a resource.
As $resource use $http and you have config a httpProvider globaly you will call your interceptors' func when you use your resource.
Second question:
You can not set an interceptor to a concrete $http object, they (interceptors) are set globally.
(Even if you set the interceptor before your module definition and then you remove it, you can not know the execution order)
What you can do if you do not want to override the interceptor property in each $resource action (as you write in your question) you can improve your interceptor.
app.factory('userLoadingInterceptor', function () {
//Code
return {
request: function(){
//Check if your are working with a url related with users
// and if so do things...
}
});
From the docs:
The interceptor object has two optional methods - response and responseError
I don't know what you want to achieve but generic HTTP interceptors might be an alternative.
A generic HTTP Interceptor should do what you want. You can find a sample here: Handle HTTP 302 response from proxy in angularjs.

Custom $templateCache loader

This is a question about template loading customization in $templateCache.
Goal is handling transport layer, exactly:
Ability to modify template url.
Ability to handle transport errors and timeouts.
How can be $templateCache loader modified with custom transport wrapper?
Prefferably at global application level, i.e. directives should't know about this modification.
You could use a $http interceptor for this. You could use the request interceptor to change the URL, and the responseError interceptor to deal with errors. A simple implementation is below, that you would have to change to exactly how you want the URL to be modified and how errors are handled.
app.factory('TemplateInterceptor', function($injector, $window, $q, $timeout) {
return {
'request': function(config) {
// Test if is a template
var isTemplate = config.url.match(new $window.RegExp("^/?templates/"));
// Save in config, so responseError interceptor knows
config.TemplateInterceptor = config.TemplateInterceptor || {};
config.TemplateInterceptor.isTemplate = isTemplate;
if (isTemplate) {
config.url = '/modified-url' + config.url;
}
return config;
},
'responseError': function(rejection) {
// Avoid circular dependency issues
var $http = $injector.get('$http');
// If a template, then auto-retry after 1 second
return !rejection.config.TemplateInterceptor.isTemplate
? $q.reject(rejection)
: $timeout(angular.noop, 1000).then(function() {
return $http(rejection.config);
});
}
}
});
Registered as:
app.config(function($httpProvider) {
$httpProvider.interceptors.push('TemplateInterceptor');
});

AngularJS: DRY $http .error()

So I have a bunch of controllers that do $http requests
but in every $http request i have a .error(function(data...){//always the same})
How could I build an.. "abstract class" for $http?
This here would be the always repeating code
.error(function(){
$scope.flashes = {
server: {
type: "danger",
message: "There was a server error processing your request. Please try again later."
}
};
})
I add the same concern few weeks ago and i came up with this solution :
I first created a custom service intercepting every http requests made :
.factory('HttpInterceptor', ['$q', '$rootScope', function($q, $rootScope) {
return {
// On request success
request : function(config) {
// Return the config or wrap it in a promise if blank.
return config || $q.when(config);
},
// On request failure
requestError : function(rejection) {
//console.log(rejection); // Contains the data about the error on the request.
// Return the promise rejection.
return $q.reject(rejection);
},
// On response success
response : function(response) {
//console.log(response); // Contains the data from the response.
// Return the response or promise.
return response || $q.when(response);
},
// On response failure
responseError : function(rejection) {
//console.log(rejection); // Contains the data about the error.
//Check whether the intercept param is set in the config array. If the intercept param is missing or set to true, we display a modal containing the error
if (rejection.config && typeof rejection.config.intercept === 'undefined' || rejection.config.intercept)
{
//emitting an event to draw a modal using angular bootstrap
$rootScope.$emit('errorModal', rejection.data);
}
// Return the promise rejection.
return $q.reject(rejection);
}
};
}]);
I also defined a custom config property 'intercept' that i can add to the $http config object. It is useful when I don't want to apply this behavior on a particular request.
E.g :
var registerResource = $resource('/registration/candidate/register', {}, {query:
{method:'POST', isArray: false, intercept: false }
});
In order the have a flexible solution, it is also important to not forget to do :
return $q.reject(rejection);
So you can still use the error callback on your promise in your controller if you want to combine both ways (interception + manual handling)
Finally, I added this service to my application :
app.config(['$httpProvider', function($httpProvider) {
// Add the interceptor to the $httpProvider to intercept http calls
$httpProvider.interceptors.push('HttpInterceptor');
}]);
I simplified the service but you can also use it for many things. Personally, I also use it to :
Make sure to not fire duplicate http requests (if the user click a lot on a submit button).
Draw an alert at the beginning of an http call and close it at the end to inform the user that is treatment is processing (export of data for instance).
PS: The official documentation mention this interceptor
You could do something like this:
app.service('myHttp', function($http){
return function($scope, httpParameters){
var httpPromise = $http(httpParameters);
httpPromise.error(function(){
$scope.flashes = {
server: {
type: "danger",
message: "There was a server error"
}
}
});
};
});
app.controller('MainCtrl', function($scope, myHttp) {
myHttp($scope, {method: 'GET', url: 'www.google.com'});
});

Resources