I am sort of new to Angularjs and I am trying to find the optimal structure to do such flow:
Get an access_token from my server asynchronously
Store it in a provider as a variable to be used in the future
Make async calls to the third party server with the access_token
My factory currently looks like this
app.factory('SomeFactory',['$resource', function($resource){
//access_token only needs to be set once
var access_token = 0;
var request = $resource('/my/server/').get();
request.promise.then(function(result){
access_token = result;
}
);
return $resource('https://third.party/:token',{token: access_token});
}])
I can't set the access_token, the call back function is to be destroyed somehow.
Also how can I form a chain, so that third party cannot be called until the access_token has been set first?
try this
app.factory('SomeFactory',['$resource, $q', function($resource, $q){
var service = {};
//access_token only needs to be set once
var access_token = null;
getAccessToken = function() {
var deferred = $q.defer();
if (access_token) {
deferred.resolve(access_token);
} else {
$resource('/my/server/').then(function(result){
access_token = result;
deferred.resolve(access_token);
});
}
return deferred.promise;
}
service.callThirdParty = function() {
var deferred = $q.defer();
getAccessToken.then(function(access_token) {
$resource('https://third.party/:token',{token: access_token}).then(function(result) {
deferred.resolve(result);
})
})
return deferred.promise;
}
return service;
}]);
Related
I've got a employeeController and a employeeFactory in the employeeFactory I receive an employee like this:
function employeeFactory(authenticationFactory,requestFactory,GLOBALS) {
var factory = {};
var vm = this;
vm.employee = {};
factory.getEmployee = function(id) {
data = {"api_token": authenticationFactory.getToken()};
url = GLOBALS.url + 'show/employee/' + id;
requestFactory.post(url, data)
.then(function (response) {
return vm.employee = response.data.result.Employee;
}, function () {
$window.location.assign('/');
});
}
return factory;
}
In my controller I'm trying to receive it like this:
console.log(employeeFactory.getEmployee($routeParams.id));
But the result is null?
When I console.log the response in my requestFactory I receive an employee object. What am I doing wrong?
Reason behind it is, you missed to return promise of requestFactory.post from factory.getEmployee method
Code
factory.getEmployee = function(id) {
data = {"api_token": authenticationFactory.getToken()};
url = GLOBALS.url + 'show/employee/' + id;
return requestFactory.post(url, data)
.then(function (response) {
return vm.employee = response.data.result.Employee;
}, function () {
$window.location.assign('/');
});
}
But even though you do it, you will not able to get value employee object printed. It will print promise object return by $http.post method/ $resource method
For getting hold on that object you need to use .then function over that promise object. like below
employeeFactory.getEmployee($routeParams.id).then(function(employee){
console.log('Employee', employee)
})
We can use deferred api in angularjs for better communication between caller and service.
factory.getEmployee = function(id) {
var deferred = $q.defer();
data = {"api_token": authenticationFactory.getToken()};
url = GLOBALS.url + 'show/employee/' + id;
return requestFactory.post(url, data)
.then(function (response) {
deferred.resolve(response.data.result.Employee);
}, function () {
deferred.reject();
});
return deferred.promise;
}
On your controller:
employeeFactory.getEmployee($routeParams.id).then(function(employee){
console.log('Employee', employee)
},function(){
$window.location.assign('/');
})
By this approach we can notify caller with some messages if your response gets delayed.
For more info see this link.
angular promises
we have use $cacheFactory for get some configuration and User data one time
like
var cache = $cacheFactory("Temp");
var getCachedData = function (url) {
var data = cache.get(url);
var deferred = $q.defer();
if (data) {
deferred.resolve(data);
} else {
readFromServer(url).then(function(result) {
cache.put(url, result);
deferred.resolve(result);
});
}
return deferred.promise;
};
return {
GetCachedData: getCachedData
};
If services get data as null then it give call to server ow it will return data from Cache but if second call come be four fist complete then he give server call so how can we use lock like C# in JavaScript .
To avoid to make multiple call to the same API, you need to make something like that (I didn't test but this should work) :
var isRetrievingData = false;
var waitingDeferred = [];
var getData = function (url) {
var data = cache.get(url);
var deferred = $q.defer();
if (data) {
deferred.resolve(data);
} else if (isRetrievingData) {
waitingDeferred.push(deferred);
} else {
isRetrievingData = true;
waitingDeferred.push(deferred);
readFromServer(url).then(function(result) {
cache.put(url, result);
for(var i = 0; i < waitingDeferred.length; i++) {
waitingDeferred[i].resolve(result);
}
});
}
return deferred.promise;
};
Here how it works :
If the data is already cached, just return it by resolving the deferred
If the data is not cached and isRetrievingData is false, launch the request and set isRetrievingData to true
If the data is not cached and isRetrievingData is true, that means that another call is in progress so don't to anything, just add the deferred in an array
When the only call is finished, to send data to each caller, you have just to resolve each deferred registered in the waitingDeferred array.
Is that good for you ?
I have the following angularjs service
var assetLookUpTransformService = function($http, $q) {
this.data = null;
var self = this;
this.doStuff = function() {
var defer = $q.defer();
if (self.data) {
defer.resolve(self.data[0].DisplayName);
} else {
$http({
url: '../AssetLookup/GetAssetLookUp',
method: "GET"
}).success(function(response) {
self.data = response;
defer.resolve(self.data[0].DisplayName);
});
}
return defer.promise;
}
}
It works ok, in that once the $http request has returned all subsequent calls to "doStuff" return data from data rather than make a new request.
Then problem I have is that I am making a bunch of calls right after each other on page load. And what is happening is that the first call to doStuff will make an $http request, but also any calls to dostuff that happen before the first http request returns will also make a $http request.
Is there a way to make the calls to doStuff "wait" for the outstanding $http request to return before making there own.
Or is there a way to ensure the $http request only ever happens once?
Cache the promise, not the data. The data is encapsulated in the promise and will always be returned to whatever requests it.
var assetLookUpTransformService = function($http, $q) {
var self = this;
this.doStuff = function() {
if (!self.deferred) {
self.deferred = $q.defer();
$http({
url: '../AssetLookup/GetAssetLookUp',
method: "GET"
}).success(function(response) {
self.deferred.resolve(response[0].DisplayName);
});
}
return self.deferred.promise;
}
}
Why don't you just make each of those calls after the first call to doStuff?
Example:
this.doStuff = function(afterRequests) {
var defer = $q.defer();
if (self.data) {
defer.resolve(self.data[0].DisplayName);
} else {
$http({
url: '../AssetLookup/GetAssetLookUp',
method: "GET"
}).success(function(response) {
self.data = response;
defer.resolve(self.data[0].DisplayName);
//make each request in here.
afterRequests.forEach(function(request) {
$http(request).success(function(response) {
//use self.data here
});
});
});
}
return defer.promise;
}
If for some reason you have to make doStuff at the same time (e.g. it's very long running), then you could use $rootScope.$broadcast to target your controllers which may need to render the data.
I have an Angular app, and in the controller I need to call a function which makes two http get requests, and I need this function to return these values just when they are resolved. I cannot make them in the $routeProvider resolve because this function needs a value obtained in the same controller.
I show part of the controller:
function myController(info) {
var vm = this;
vm.name = info.data.name;
vm.extra_data = MyService.getExtras(name);
}
And here is part of the code of the service:
function myService($http, $q, API_EVENT1, API_EVENT2) {
return {
getExtras: getExtras
}
function getExtras(name) {
var request1 = API_EVENT1 + '/' + name;
var request2 = API_EVENT2 + '/' + name;
}
}
The function is not complete due to I have tried several methods, but I wasn't successful at all.
My problem is how to return (in getExtras function) both of the results of the requests. I have tried using $q.defer, $q.all and promises, but I'm not able to achieve it.
Using $q.all, for example, I got to retrieve the result, but when I call the function, the object extra_data is undefined, due to the requests have been executed asynchronously.
Thank you in advance for your responses.
I would use $q.all to return combined promise from Service, and process result in controller. e.g.:
function myService($http, $q, API_EVENT1, API_EVENT2) {
return {
getExtras: getExtras
}
function getExtras(name) {
return $q.all([
$http({method: 'GET', url: API_EVENT1 + '/' + name}),
$http({method: 'GET', url: API_EVENT2 + '/' + name})])
);
}
}
function myController(info) {
var vm = this;
vm.name = info.data.name;
MyService.getExtras(name).then(function(data) {
vm.extra_data.first = data[0];
vm.extra_data.second = data[1];
});
}
I have a service that runs an ajax query which upon success forwards that to a second function to parse the response into an object. The function that is initially called returns a promise using the $q library before the promise is resolved, which happens in the second function that parses the response into an object, and passes that object as a parameter to the resolve method. My controller which activates the service uses the .then method to log out the response for testing. This all works great the first time, but consecutive times it returns the resolve from the intial call, before the resolve is called a second time. How can I prevent this from happening?
Here is my code
app.controller("login", ['$scope','XMLMC', function ($scope,api) {
$scope.login = function() {
//This is bound to an ng-click directive in the current route template
var params = {
selfServiceInstance: "selfservice",
customerId: $scope.username,
password: $scope.password
};
var authenticated = api.request("session","selfServiceLogon",params).then(function(response) {
console.log(response);
//log the response once the promise is resolved or rejected
});
};
}]);
app.factory("XMLMC", ['$http', '$q', function ($http, $q) {
function XMLMC($http, $q) {
$http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
var def = $q.defer();
var P = def.promise;
var that= this;
this.prepareForPost = function(pkg) {
return JSON.stringify(pkg);
};
this.request = function(service, request, params, host, newsession) {
if(request === "analystLogon") {
newsession = true;
}
var call = {
service: service,
method: request,
params: params
};
if(host) {
call.host = host;
} else {
call.host = "localhost";
}
if(newsession) {
call.newsession = "true";
}
var pkg = {
contents: this.prepareForPost(call)
};
$http.post('php/XMLMC/api.php', jQuery.param(pkg)).success(function (response,status) {
that.consume(response, def);
//consume the response, pass the deferred object to resolve later
}).error(function (response,status) {
def.reject(response,status);
});
return P; //return the promise, not the resolved object
};
this.consume = function(response, defer) {
console.log(response);
//log the response that was received. For some reason this log happens after the log in the controller on subsequent calls to this service, not the first call.
var resp = response[0],
digested = {},
i;
digested.status = resp["attrs"]["STATUS"];
var params = resp["children"][0]["children"];
for(i=0; i < params.length; i++) {
var key = params[i]["name"];
var val = params[i]["tagData"];
digested[key] = val;
}
defer.resolve(digested);
//resolve at this point, after the response has been consumed and parsed.
};
}
return new XMLMC($http, $q);
//return new instance of this object for ease of use in controller
}]);
try this schema, i removed the creation of the deferred object and returned the promise chain initited by $http.post, not sure if will work but it feels like it should since $http returns a promise.
app.factory("XMLMC", ['$http', '$q', function ($http, $q) {
function XMLMC($http, $q) {
$http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
this.prepareForPost = function(pkg) {
return JSON.stringify(pkg);
};
this.request = function(service, request, params, host, newsession) {
if(request === "analystLogon") {
newsession = true;
}
var call = {
service: service,
method: request,
params: params
};
if(host) {
call.host = host;
} else {
call.host = "localhost";
}
if(newsession) {
call.newsession = "true";
}
var pkg = {
contents: this.prepareForPost(call)
};
return $http.post('php/XMLMC/api.php', jQuery.param(pkg))
.then(that.consume)
};
consume = function(response) {
console.log(response);
var resp = response[0],
digested = {},
i;
digested.status = resp["attrs"]["STATUS"];
var params = resp["children"][0]["children"];
for(i=0; i < params.length; i++) {
var key = params[i]["name"];
var val = params[i]["tagData"];
digested[key] = val;
}
return $q.resolve(digested);
//resolve at this point, after the response has been consumed and parsed.
};
}
return new XMLMC($http, $q);
//return new instance of this object for ease of use in controller
}]);