I am trying to read data from json and wait until data will be fetched into $scope.urls.content. So I write code:
$scope.urls = { content:null};
$http.get('mock/plane_urls.json').success(function(thisData) {
$scope.urls.content = thisData;
});
And now I am trying to write something like callback but that doesn't work. How can i do that? Or is there any function for this? I am running out of ideas ;/
Do you mean that ?
$http.get('mock/plane_urls.json').success(function(thisData) {
$scope.urls.content = thisData;
$scope.yourCallback();
});
$scope.yourCallback = function() {
// your code
};
You want to work with promises and $resource.
As $http itself returns a promise, all you got to do is to chain to its return. Simple as that:
var promise = $http.get('mock/plane_urls.json').then(function(thisData) {
$scope.urls.content = thisData;
return 'something';
});
// somewhere else in the code
promise.then(function(data) {
// receives the data returned from the http handler
console.log(data === "something");
});
I made a pretty simple fiddle here.
But if you need to constantly call this info, you should expose it through a service, so anyone can grab its result and process it. i.e.:
service('dataService', function($http) {
var requestPromise = $http.get('mock/plane_urls.json').then(function(d) {
return d.data;
});
this.getPlanesURL = function() {
return requestPromise;
};
});
// and anywhere in code where you need this info
dataService.getPlanesURL().then(function(planes) {
// do somehting with planes URL
$scope.urls.content = planes;
});
Just an important note. This service I mocked will cache and always return the same data. If what you need is to call this JSON many times, then you should go with $resource.
Related
I had been using a angular service in which the data was returning just fine.
However, I wanted to instead call directly to the json file except now it doesn't like the data.
Working version
Controller code:
var confirm = this;
confirm.booking = airConfirmationService.getTestData();
Service code :
.factory('airConfirmationService', airConfirmationService);
var confirm = {};
confirmed.getTestData = function () {
return {
"flightData": [
{
"MultiCarrier": false,
// etc...
However I am switched to a service with .service and calling the location of the .json file up directly.
Not working ( well, it pulls the data but returns in a way that I don't understand)
.service('airConfirmationService', function ($http) {
this.getTestData = function () {
return $http({
url: '../apps/temp/Api_Responses/confirm.booking.json',
method: "GET"
})
}
});
Then in controller
var confirm = this;
confirm.booking = airConfirmationService.getTestData();
console.log(confirm.booking) // Picture attached shows how the data looks
// My attempt at getting "at" the data ...
//var temp = [];
//temp = airConfirmationService.getTestData();
//confirm.booking = temp.d.Data;
UPDATE
While this code below "works" I have a feeling that not doing "q" /defer / .then will be bad...
This code works in the controller calling the service, but how can i add/change to have q/defer and/or .then ?
var getData = airConfirmationService.getTestData();
getData.success(function(data) {
confirm.booking = data;
});
Angular $http is Asynchronous. The network operation to get the data from the server may take some time, so angular does not expect your app to wait until the operation is complete, stuck hung until the data is available. Therefore, a promise object is used as a placeholder for the data. Promises have a .then() function which tells the promise that it should execute some other code after the operation is complete. They also provide a .catch() function for when something goes wrong and the data isn't returned.
The correct way to use $http is:
var confirm = this;
confirm.testData = {};
airConfirmationService.getTestData()
.then(function(response) {
confirm.testData = response.data;
})
.catch(function(){
//something went wrong
});
communicating with a REST service in ionic, I'ld like to have functions similar to this
function ListCategories_Request(){
rqst=_BuildRequest("ListCategories");
rqst.Data={extraParam1:1,
extraParam2:2
}
return $http({...ValidParameters including the rqst...}).then(GetResult,Request_onError)
}
For each function of the REST-Service, I would build a similar java-script function.
Now, as the sent request is unique, the received result needs to be handled unique, too. The REST-Service-Based analysis of the result is added to the .then-chain, but updating the parent class and the UI needs to happen in the parent class.
So, I would like to do calls like
ListCategories_Request().then(function(res){ UpdateCategories()});
ListFrames_Request().then(function(res){ UpdateFrames()});
The current problem is, that UpdateCategories() is called, before the result of the http-Request is analysed.
So, how do I prevent return $http(...).then(GetResult,OnError) to return, before the specific function inside GetResult is called?
Code of GetResult:
function GetResult( res){
if(res.status==200)
{
if( (res.data!=={}) && (res.data.Data!=={}) )
{
return AnalyzeResult(res.data);
}
}
};
While AnalyzeResult is like:
function AnalyzeResult(Result)
{
Func=Result.Func;
switch(Func.toUpperCase())
{
case "LISTCATEGORIES":
erg = ListCategories_Result(Result);
break;
case "LISTFRAMES":
erg= ListFrames_Result(Result);
break;
default:
erg = {};
console.log("UnKnown Result!");
}
FinalizeRequest(Index);
}
return erg;
}
So, I do not really get, where my mistake is. How do I prevent ListCategories_Request or ListFrames_Request from returning too early?
Thank you and best regards
Frank
You are better off using $q (given you are using angular anyway via ionic). That way you can use promises to do what you want to do. Here's an example that I put together to demonstrate the idea:
var app = angular.module("TestApp",[]);
app.controller("TestController", function($scope, $http, $q){
$scope.message = "Deferred Example";
var deferred = $q.defer();
function getAllPosts(extractor) {
$http.get("https://jsonplaceholder.typicode.com/posts")
.then(function(data){
console.log("Data is: ", data);
$scope.postIds = extractor(data.data);
deferred.resolve($scope.postIds);
});
return deferred.promise;
}
function extractPostIds(data) {
console.log("Exracting the data: ", data);
return data.map(function(post){
return post.id;
})
}
function squarePostIds(postIds) {
console.log("Squaring post ids: ", postIds);
$scope.squaredPostIds = postIds.map(function(id){ return id*id;});
}
getAllPosts(extractPostIds)
.then(function(postIds){
squarePostIds(postIds);
});
});
And here's the JSBin for this: https://jsbin.com/fugeyad/3/edit?html,js,output
Update: Adding some links to read about promises.
http://andyshora.com/promises-angularjs-explained-as-cartoon.html
http://www.html5rocks.com/en/tutorials/es6/promises/
I try to get some important things like: companyid,employeeid etc. with every request that a user makes. So this has to be received before everything else is done.
After that the user receives information based on his companyid that he sets with every request (get/company/{companyid}).
The problem that I have is that the response for receiving the companyid takes to long and angular already tries to make a request to (get/company/{companyid}) obviously there is no companyid yet.
I've tried to fix this whit promise but it's not working.
Here I try to receive some important information about the user(that I do with every request) :
Service
(function () {
angular.module('employeeApp')
.service('authenticationservice', authenticationservice);
function authenticationservice($http,$location,authenticationFactory,$q,GLOBALS,$cookies) {
this.validateUser = function () {
var vm = this;
vm.deferred = $q.defer();
data = {"api_token": api_token};
return $http.post(GLOBALS.url+'show/employee/' + $cookies.get('employeeid'),data)
.success(function(response)
{
vm.deferred.resolve(response);
})
.error(function(err,response)
{
vm.deferred.reject(err);
});
return vm.deferred.promise;
}
}
})();
Routes file
(In my routes file I use the authenticationservice to set all important users variables.)
employeeAppModule.run([
'authenticationservice',
'constants',
function(authenticationservice,constants) {
authenticationservice.validateUser()
.then(function(response)
{
constants.companyid = response.result.Employee;
constants.role = response.result.Role;
constants.name = response.result.FirstName;
console.log('test');
},
function(response){
console.log('error');
});
}
]);
So the problem is that the user information is set to late and angular already goes to my homeController where he uses the companyId that is not being set yet.
Thankyou
The problem in your current code is return $http.post are having two return statement in your validateUser method. Which is returning $http.get before returning return vm.deferred.promise; & that why customly created promise doesn't get returned from your method. Though by removing first return from $http.get will fix your problem, I'd not suggest to go for such fix, because it is considered as bad pattern to implement.
Rather I'd say, you should utilize promise return by $http method, & use .then to return data to chain promise mechanism.
Code
function authenticationservice($http, $location, authenticationFactory, $q, GLOBALS, $cookies) {
this.validateUser = function() {
var vm = this;
data = {
"api_token": api_token
};
return $http.post(GLOBALS.url + 'show/employee/' + $cookies.get('employeeid'), data)
.then(function(response) {
var data = response.data;
retrun data;
}, function(err) {
return $q.reject(err);
});
}
}
To make sure that $ http return a $ promise object you need to check that the action in the controller returns a value and it is not a void action.
I have done a lot of reading around this, but ultimately the tutorials and guides I have found differ too much for me to get a decent grasp on this concept.
This is what I want to achieve:
1) Simple http request from our server [Any API for demonstration]
2) Run a function with data from (1). [Remove a property from the object]
3) Use result and length of (2) to run a loop of $http requests to our server. [Or any server]
4) This will result in 6 different objects. Run a function on these 6 objects. [Add a property]
5) Once ALL of this is done, run a separate function [Log "finished"]
How can this be achieved using promises? How do I pass data from (1) via a promise to (2)? Is this the right way to achieve what I need to do?
If anyone can show me how this should be structured it would be immensely helpful; I have kept the functions as simple as possible for this question.
Yes, promises are very nice to structure solutions for this kind of problems.
Simplified solution (more or less pseudo-code):
$http(...)
.then(function(response) {
// do something with response, for example:
var list = reponse.data.list;
// return it so that you can use it in the next 'then'.
return list;
})
.then(function(list) {
var promises = [];
angular.forEach(list, function(item) {
// perform a request for each item
var promise = $http(...).then(function(itemResponse) {
itemResponse.extraProperty = true;
return itemResponse;
});
// we make an array of promises
promises.push(promise);
});
// combine all promises into one and return it for the next then()
return $q.all(promises);
})
.then(function(itemsList) {
// itemsList is now an array of all parsed item responses.
console.log(itemsList);
});
(Hopefully this is right, I did not tested it.)
As you can see, you can return values in a callback to pass it to the next then(), or you can pass a promise, and this will result in calling the next callback when it resolves. $q.all() is used to combine multiple promises into one and resolve if all are resolved.
Edit: I realised that you can optionally leave out these three lines:
return list;
})
.then(function(list) {
But it is nice syntax though, because the separation of tasks is more visible.
Check code below, it could contains syntax error, the important is the structure. Step3 contains multiple(6) $http requests, it waits until the last request response to return a unique response object (array) containing response for each $http requets.
//Step 1
var Step1 = function () {
$http.get('api/controller').success(function (resp) {
var object1 = resp;
Step2(object1);
Step3(object1).then(function (resp) {
//resp.data is an array containing the response of each $http request
Step4(resp);
Step5();
});
});
}
//Step2
var Step2 = function(obj){
//do whatever with the object
}
//Step3
var Step3 = function (object1) {
var call = $q.defer();
var get1 = $http.get(object1[0].url);
var get2 = $http.get(object[1].url2);
//...
var get6 = $http.get(object[5].url6);
$q.all([get1, get2,..get6]).then(function (resp) {
call.resolve(resp);
});
return call.promise;
}
//Step4
var Step4 = function (resp) {
for (var i=0; i<resp.data.lenght;i++){
DoWhatEver(resp.data[i]);
};
}
//Step5
var Step5 = function () {
alert("Finished");
}
Step1(); //Call Step1 function
Don't know why you have difficulty implementing this, but maybe $q.all() is what you're missing:
var config1={method:'GET',url:'/api/...'};
$http(config1).success(function(resultsFrom1){
functionForResultsOf1(resultsFrom1);
})
var functionForResultsOf1 = function(resultsOf1){
//remove something from the result, assuming this is a synchronous operation
resultsOf1.splice()...;
var promises=makePromises(*pass whatever you want*);
$q.all(promises).then(function(aggregateOfAllCallsToServer){
angular.forEach(aggregateOfAllCallsToServer,function(data){
//do something to data from each call to the server
})
console.log("finished");
})
}
var makePromises = function(serverUrls){
var promises = [];
angular.forEach(serverUrls, function(url) {
var promise=$http({
url : '/api/'+url,
method: 'GET',
})
promises.push(promise);
});
return $q.all(promises);
}
I'm trying to create an angular js service that will fetch data from the server if update needed or return cached array if there is no update. In both cases service should return a promise. Service code:
getLikesForMe = function() {
var defer = $q.defer(), prom = defer.promise;
if (angular.isUndefined(this.likesForMe) ||
updateStatus.likesForMe === true) {
var that = this;
prom = $http.get(API_URL + 'likes-to.json')
.then(function(result){
updateStatus.likesForMe = false;
that.likesForMe = result.data;
});
} else {
defer.resolve(this.likesForMe);
}
return prom;
}
Current controller code:
MainUser.getLikesForMe().then(function(result) {
$scope.likesList = result;
});
Prefered controller code:
$scope.likesList = MainUser.getLikesForMe();
But currently it only works after second function (getLikesForMe()) call, on the first - the list is empty and the "result" variable is undefined.
I'm new to deferred objects and my english is pretty poor, but I hope you understand where the mistake is. Thanks a lot!
You have 2 issues
On the first call, the promise you return will be resolved with undefined as you're not returning anything from the then success callback from your call to $http.get. If you return that.likesForMe from that callback, I suspect it will work as you expect. You should probably read up on chaining promises. (Shameless plug: I wrote a blog post on AngularJS promises which contains sections on chaining)
The code is quite complicated for what it does, and you almost-never have to create a promise via $q.defer() if all you're doing to working with existing promises. It usually makes things more complicated and harder to deal with errors. You can also use $q.when() to create a promise from a non-promise value. So I propose something like
getLikesForMe = function() {
var that = this;
var useCache = !updateStatus.likesForMe && !angular.isUndefined(this.likesForMe);
return useCache ? $q.when(that.likesForMe) : $http({
method: 'GET',
url: API_URL + 'likes-to.json'
}).then(function(results) {
updateStatus.likesForMe = false;
that.likesForMe = results.data;
return that.likesForMe;
});
});
You could also read up in the docs for $http cache to see if you could come up with a solution that uses it.
If you prefer the later version of the controller code then you can't return a promise from the service.
You could return an empty object that would be augmented when you got back from the http call.
You'd then have to issue a $rootScope.$apply() so your controller is notified.