AngularJS get value from API only if not already set - angularjs

I have service to get some data from API and serve them to application.
Simple function like this:
getEnvironmentStatus() {
var _this = this;
var req = {
method: "GET",
url: "/api/system/hosting",
headers: {},
data: {}
}
return _this.$http(req);
}
In some other place I have:
determineHostingEnv() {
var _this = this;
this.$env.getEnvironmentStatus()
.then(function(response){
_this.EnvHositng = response.data.cloud_hosted;
}, function(error) {
});
}
If I need the same information in other place (other controller), I would need to call api again.
How can I make getEnvironmentStatus() function to call API only once and store data in local variable, so it can serve that variable next time it is asked for it, instead of calling API?
Also, what if that value will get requested a few times before the first API will return value? Can I prevent calling that API a few times?

One can cache the promise:
httpPromiseCache = null;
getEnvironmentStatus() {
var _this = this;
var req = {
method: "GET",
url: "/api/system/hosting",
headers: {},
data: {}
}
if (!_this.httpPromiseCache) _this.httpPromiseCache = _this.$http(req);
return _this.httpPromiseCache;
}
The service will only execute the HTTP request once.

Related

Multiple GET requests in Angularjs

I want to make multiple ajax calls from different urls in football-data.org
API.
Here's my code sample:
angular.module('liveFootball', ['ionic'])
.constant('urls', {
BASE: 'http://api.football-data.org/',
BASE_API: 'http://api.football-data.org/v1/soccerseasons/',
TEAM_URL: 'http://api.football-data.org/v1/teams',
HEAD: {
'X-Auth-Token': 'e7486677a2dd4260b7aeb8a464749e80'
}
});
getAllFixtures: function(leagueID){
var getAllFixtures = {
method: 'GET',
url: urls.BASE + "fixtures?timeFrame=n14",
headers: urls.HEAD
}
return $http(getAllFixtures);
},
Is there a way I can include another url in this call?
Thanks.
It's not possible to have more than one url field in the $http config object, but you can send the three requests and use Promise.all() $q.all to await their responses. The response will be a promise which when you .then() will have an array containing all the responses.
getAllFixtures: function(leagueID){
var sources = [
urls.BASE,
urls.BASE_API,
urls.TEAM_URL
];
var promises = [];
for(var i=0; i<sources.length; i++){
promises.push($http({
method: 'GET',
url: sources[i] + "fixtures?timeFrame=n14",
headers: urls.HEAD
}));
}
return ̶P̶r̶o̶m̶i̶s̶e̶.̶a̶l̶l̶ $q.all(promises);
}
There is no way you can include another url , you have to call it again. You can use $q.all in angular to make multiple request at once.
For example:
var request = [getAllFixtures('10'), getAllFixtures('11)];
$q.all(request).then(function (value) {
},function(err){
}

Cancel Previous Ajax Request and Start New One in Angularjs

following solution is not working for me some how
How to cancel an $http request in AngularJS?
above solution effect - abort new ajax call also,it should be allow new ajax call and abort old ajax call
my code
$scope.callajax = function () {
var canceller = $q.defer();
var data = {};
data['id'] = id;
$http({
method: "post",
url: url,
data: $.param(data),
timeout: canceller.promise,
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).success(function (response) {
});
canceller.resolve();
};
i am calling this function,if i call this function two time at a time then it should be abort first ajax call and fire new ajax call
The problem with your code is that the canceller.resolve() gets called and cancels the $http immediately. The following works by flagging if an ajax call is active and cancelling the call.
JSFiddle
var canceller,
isSending = false;
$scope.callajax = function () {
console.log("callajax");
if(isSending) {
canceller.resolve()
}
isSending = true;
canceller = $q.defer();
var data = {
html: "<p>Text echoed back to request</p>",
delay: 5
}
$http({
method: "post",
url: "/echo/html",
data: data,
timeout: canceller.promise
}).success(function (response) {
isSending = false;
console.log("success");
}).error(function(data, status) {
isSending = false;
console.log("error");
});;
};
Since, you have the cancelar object inside the function and also calling it, the callajax function will call and then cancel it everytime. To achieve what you want you should be trying something like this -
$scope.cancelHandles = [];
$scope.currentIndex=0;
$scope.callajax = function () {
var cancelar = $q.defer();
$scope.cancelHandles.push(cancelar);
//rest of your code goes here except the last line cancelar.resolve();
$scope.currentIndex = cancelHandles.length;
}
$scope.cancelAjaxCall = function(index) {
cancelHandles[index].resolve();
}
The function call - callajax shall be returning the index in the array of cancel handlers which will map to the number of time you have called the ajax function.
There is a separate function called cancelAjaxCall which will accept an index i.e. the nth number of ajax call which you want to cancel.
If you want to cancel the 3rd call from now in the past simply subtract 3 from the $scope.currentIndex and call the cancelAjaxCall function i.e. cancelAjaxCall($scope.currentIndex-3);

Restangular save and retry after refresh

I'm working on an SPA that is usually online but can go offline and I have to keep a log of all requests to an API. When certain requests fail I should retry them later.
I've got a local DB to save all requests. When my app starts I retrieve them all and retry the ones marked to.
What I need is a way to config a Restangular object based on what I already sent. I have a response interceptor and I'm saving the restangular response object.
{
config: {
headers: Object,
method: "GET",
params: Object,
transformRequest: Array[1],
transformResponse: Array[1],
url: "..."
},
data: {...},
headers: {...},
status: 200,
statusText: "OK"
}
Is there a function to create a Restangular with the given config object?
Thanks
If i would doing this i would setup setErrorInterceptor
var yourLocalDb = function($http) {
var failedRequests = [];
this.store = function(request) {
this.failedRequests.push(request);
}
this.retry = function() {
var self = this;
angular.forEach(this.failedRequests,function(request) {
$http(request.response.config).then(function() {
//in case of success
self.failedRequests.splice(self.failedRequests.indexOf(request),1);
request.responseHandler.apply(undefined,arguments);
}), request.deferred.reject);
});
}
}
Restangular.setErrorInterceptor(function(response, deferred, responseHandler) {
yourLocalDb.store({
response : response,
responseHandler : responseHandler,
deffered : deffered
});
}
then when you have connection you can just call
yourLocalDb.retry();
Not tested, but it should give you clue.

angularjs resource limit changes after first call

Problem description
Im using the angular resource to get data from my server. I've extended it a bit to make sure all of my resources have security headers.
Problem is that on the second get request and on, my get requests are sent with limit=0, and only the first get request is sent correctly (with limit=12).
Code part
This is my base resource factory (for making sure all resource contain the keys and everything):
app.factory('SecuredFactory', function($resource){
var DEFAULT_ACTIONS = {
'get': {method:'GET'},
'query': {method:'GET', isArray:true},
};
var DEFAULT_PARAMS = {
'limit': 12,
'format': 'json'
};
for(var key in DEFAULT_ACTIONS){
DEFAULT_ACTIONS[key]['headers'] = <headers object>;
}
var securedResource = function(url, paramDefaults, actions){
for (var attrname in actions) {
DEFAULT_ACTIONS[attrname] = actions[attrname];
}
for (var attrname in paramDefaults) {
DEFAULT_PARAMS[attrname] = paramDefaults[attrname];
}
var defaultResource = $resource(url, DEFAULT_PARAMS, DEFAULT_ACTIONS);
return defaultResource;
};
return securedResource;
});
And this is an example of how I creat a specific factory out of the secured one:
app.factory('QuestionFactory', function(SecuredFactory, Constants){
var url = Constants.SERVER_URL + 'question/';
var Task = SecuredFactory(url);
return Task;
});
And this is finally how I use it, for example:
// filtering example (not important for this matter):
var filtering = {author: "Daniel"};
var contents = [];
var resource = QuestionFactory;
resource.get(filtering, function (res) {
// success fetching
$scope.contents = $scope.contents.concat(res['objects']);
}
// failed fetching
, function (err) {
}
);
The requests
first request:
question?format=json&limit=12&offset=0
second request and on:
question?format=json&limit=0&offset=0
My problem was that the DEFAULT_PARAMS variable was declared as global. I didn't realize that invoking the secured factory with {limit: 0} will override the global, therefore changing the limit to 0 for ALL of my resources.
Changing the securedFactory to a service and moving the "globals" into the returned function solved it. Had to add new ofcourse before every securedService call.

Can someone give me an example on how I can call $resource directly?

In my code I have:
var EntityResource = $resource('/api/:entityType', {}, {
postEntity: { url: '/api/:entityType/', method: 'POST' },
getEntity: { url: '/api/:entityType/:entityId', method: 'GET' },
putEntity: { url: '/api/:entityType/:entityId', method: 'PUT' },
deleteEntity: { url: '/api/:entityType/:entityId', method: "DELETE" },
getEntities: { url: '/api/:entityType/:action/:id', method: 'GET', isArray: true },
});
Then I am using the following to get data:
getProjects: function (
entityType,
deptId) {
var deferred = $q.defer();
EntityResource.getEntities({
action: "GetProjects",
entityType: entityType,
deptId: deptId
},
function (resp) {
deferred.resolve(resp);
}
);
return deferred.promise;
},
and the following to call getProjects:
entityService.getProjects(
'Project',
$scope.option.selectedDept)
.then(function (result) {
$scope.grid.data = result;
}, function (result) {
$scope.grid.data = null;
});
I think the intermediate function getProjects is not needed and I would like to directly use $resource.
Can someone give me some advice on how I could do this? I looked at the AngularJS documentation for $resource and it's not very clear for me.
$resource calls by default return empty arrays and then fill them up when the response is received. As mentioned in documentation
It is important to realize that invoking a $resource object method
immediately returns an empty reference (object or array depending on
isArray). Once the data is returned from the server the existing
reference is populated with the actual data.
There are default 5 methods already defined on resource, get,save,query,remove,delete. You can directly call these rather than defining your own as you have done like postEntity, but the url template remains the same.
So once you define resource like this
var entityResource = $resource('/api/:entityType');
you can make calls like
var entity=entityResource.get({entityType:1},function(data) {
//The entity would be filled now
});
See the User example in documentation
If you want to return promise then you have to wrap the calls into your your service calls like you did for getProjects.
Update: Based on your comment, the definition could be
var entityResource = $resource('/api/:entityType/:action/:id')
Now if you do
entityResource.get({},function(){}) // The query is to /api
entityResource.get({entityType:'et'},function(){}) // The query is to /api/et
entityResource.get({entityType:'et',:action:'a'},function(){}) // The query is to /api/et/a
entityResource.get({entityType:'et',:action:'a',id:1},function(){}) // The query is to /api/et/a/1
Hope it helps.
$resource does expose $promise but it is on return values and subsequent calls.

Resources