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');
});
Related
I want to send headers each time for CRUD operation from factory side.
Here is my factory
var appangular.module("LifeStyleFactModule",["ngResource"]);
app.constant("RES_URL", "http://localhost:9090/")
app.factory("CategoryFactory",function($resource,RES_URL){
var categoryinfo;
var categoryresource=$resource(RES_URL+"category/:id",{"id":"#id"},{update:{method:"PUT"}});
return{
getcategory:function(){
categoryinfo=categoryresource.query();
return categoryinfo;
},
addcategoryItem:function(categoryItem){
var category = new categoryresource(categoryItem);
category.$save(function(respdata){
categoryinfo.push(respdata);
},function(respdata){
});
},
deletecategoryItem:function(idx,id){
var category=new categoryresource({"id":id});
category.$delete(function(){
categoryinfo.splice(idx,1);
},function(){
})
},
updatecategoryItem:function(categoryItem,idx){
var category=new categoryresource(categoryItem);
category.$update({"id":categoryItem._id},function(data){
categoryinfo[idx]=data;
},function(){
})
}
}
})
the above functionality is working well. Now i want to send the token in the headers. How can i do that.
I have tried to do it by the following way
var categoryresource=$resource(RES_URL+"category/:id",{"id":"#id"},{update:{method:"PUT"},headers:{"token":"#token}});
but not getting how to send the token for CRUD operation.
Is procedure is correct, if so how can i send tokens.
Else let me know the way.
Instead of above method i tried the following way as
$resource(RES_URL+"category",{},{query:{method:"get",isArray:true,headers:{"token":token}}}).query({},function(res){});
this is working but the procedure for the first procedure.
Please after answering mark it as duplicate or down vote
dont say ( / { such things are missing.
The best solution as to me is to use interceptor. Here is a way to send token in headers, I've used in one of my projects.
angular
.module('app.core')
.config(config);
config.$inject = ['$httpProvider'];
function config($httpProvider) {
$httpProvider.interceptors.push(interceptor);
}
interceptor.$inject = ['$q', '$injector', 'AuthModel'];
function interceptor($q, $injector, AuthModel) {
return {
request: function (config) {
config.headers.Authorization = AuthModel.token;
return config;
},
responseError: function (rejection) {
}
};
}
Added a jsfiddle to demonstrate
https://jsfiddle.net/Sergey_Mell/c47js1zc/
Just click the Send button and check the request headers in developer tools
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.
Since i'm using Oauth2 to protect my Api, i need to get a new access token before any http requets if the previous access token has expired.
I didn't used event listener much until now.
Here what i did for now (Please let me know if it is correct) :
ApplicationController.js :
app.controller('ApplicationController', function($rootScope, $scope, $localStorage, AuthService){
// Listening event apiRequested
$scope.$on('event:apiRequested', function(e) {
AuthService.token();
// Restore the access_token in case it has changed
access_token = $localStorage.getObject('access_token');
});
})
UserController.js :
$rootScope.$broadcast('event:apiRequested');
// Get Users around
return $http.post(domain+'/api/users?access_token='+access_token.key, data).then(function(response){
return response;
});
First thing i'm not sure about ... Does $http is processed if the event already executed entirely?
So since i'm not sure, i'm thinking about adding a callback.
Here the idea :
$rootScope.$broadcast('event:apiRequested', function(response){
if(response){
// Get Users around
return $http.post(domain+'/api/users?access_token='+access_token.key, data).then(function(response){
return response;
});
}
});
Please let me know if it is possible to do that or should i use something else than event listener for that case.
Why don't you use interceptors that is done to intercept HTTP request ?
In your case, you shall add this very specific behaviour into the "request" part.
See an interceptor exemple bellow:
var $myService; // Add a constant that store the service
$httpProvider.interceptors.push(['$location', '$injector', '$q', function($location, $injector, $q) {
return {
'request' : function(config){
console.log("intercept request", config.url,config)
// Your token shall be retreive in this part
return config
},
'response' : function(config){
$myService= $myService|| $injector.get('$myService'); // inject the service manually if constant is undefined
console.log("intercept response", config)
// Your token shall be retreive in this part
return config
},
'responseError': function(rejection) {
console.log("responseError intercepted" , rejection);
if (rejection.status === 403) {
return $q.reject(rejection);
} else if (rejection.status === 423) {
return $q.reject(rejection);
}else
return $q.reject(rejection);
}
};
}]);
Interceptors shall be defined into .config(["$httpProvider", function($httpProvider)
I have implemented resources in my single page angular app which fires to my REST client server. I have made different services for each resource. Now my REST server is sending a value in response header, now I want to know a proper way where I can retrieve that value from headers.
My service code:
app.service('$job', function($resource) {
var job = $resource(service_base_url+'jobs.json/:id');
return job;
});
My controller which is getting headers:
app.controllerProvider.register('JobPostsController',['$scope','$job', function($scope, $job) {
$scope.jobs = {};
$scope.job_titles = {};
$job.query(function(jobs,responseHeaders){
var headers = responseHeaders();
some_function(headers.user);
$scope.jobs = jobs.jobs;
});
}
]);
I am getting headers in my above code, but I don't want to inject it in all controllers. So is there a proper way to do it? Some single config code which will run for all future resources request or some kind of event which can be only triggered when successful resource response with 200 OK
Try interceptor.
I don't know exactly your logic. You could register a global interceptor which intercepts all requests:
angular.module('App', [])
.config(function ($httpProvider){
$httpProvider.interceptors.push(function() {
return {
'response': function(response) {
var headers = response.headers();
some_function(headers.user);
return response;
}
};
});
});
or just register an interceptor which runs only for all requests of this query.
app.service('$job', function($resource) {
var job = $resource(service_base_url+'jobs.json/:id',{}, {
'query': {
method:'GET',
isArray:true,
interceptor: {
'response': function(response) {
var headers = response.headers();
some_function(headers.user);
return response;
}
}
}
});
return job;
});
Side notes:
Should not use $ prefix for your service name as it's reserved for angular, it may conflict with angular future versions.
I guess you need .factory instead of .service
You can set up the service in a run block like:
angular.module('myApp', [])
.run(['$rootScope', '$job',function ($rootScope, $job) {
$rootScope.jobs = {};
$rootScope.job_titles = {};
$job.query(function(jobs,responseHeaders){
var headers = responseHeaders();
some_function(headers.user);
$rootScope.jobs = jobs.jobs;
});
}]);
The only drawback your service is global to the app
I would go with a base service factory. This would allow you to have common service related functionality in one place
app.factory('ServiceBase', function () {
function ServiceBase() {
this.responseHeaders = function responseHeaders(resp){
// todo
};
}
return ServiceBase;
});
app.service('$job', function($resource, ServiceBase) {
var service = function () {
// $job related functions here
};
angular.extend(service, new ServiceBase());
return service;
});
Now anything in the ServiceBase is accessible to the controller and to the service. This allows you to have common functionality, has no new injection dependencies (on the controller), and is easy to extend further.
I think angulrjs response interceptors can help u for this.
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'});
});