my angularjs code is as follows:
holidays = new Array();
var deferred = $q.defer();
servicePOST.send(appConstants.BASE_MS_URL + 'Dcrs/activityDay.php',{
"date":d
}).then(function(result) {
$scope.holidays = result;
alert(holidays.length);
deferred.resolve(result);
});
return deferred;
alert("holiday length after service post"+holidays.length);
angularjs is not sequential as it occurs to me. so this code executes in the end! this code of mine is inserted in such an order in my angularjs controller that the code down below depends on this 'holidays' array. what happens in my above code is the alert 'holiday length after service post0' pops up first and than after service post executes in the end, the length of the array is actually displayed.
now i tried using $q,promises,deffered as above but it doesn't seem to work.i have injected $q only in my angularjs controller.
is there anything wrong i have done in the above code? please help!
A promise returns another promise, so you can use this 2nd promise to have your logic performed in the order you wish. You are also complicating things in this situation with the $q service.
holidays = new Array();
var promise = servicePOST.send(appConstants.BASE_MS_URL + 'Dcrs/activityDay.php',{
"date":d
}).then(function(result) {
$scope.holidays = result;
alert(holidays.length);
return result;
});
promise.then(function(result) {
alert("holiday length after service post"+holidays.length);
});
The code is executed sequentially, it's just that the request takes time to resolve, but it starts before the alert. You should handle the request first, then alert the result inside the "then".
Related
Im a newbie in AngularJs and do not really know how to get length of response outside function.
var onUsers = function(response){
$scope.users = response.data;
console.log($scope.users.length); //here works
}
But when i try outside onUsers function
console.log($scope.users.length);
I get an error.
I think the problem is that you are trying to access $scope.users.length before your response has returned from the server. so $scope.users is still undefined. This is a classic problem of asynchronous javascript and you need to use promises for this.
Which means that you should write the code that you are executing outside onUsers in the promise callback.
Read this: https://docs.angularjs.org/api/ng/service/$q
You can use $scope.$watch for run a function when the variable change.
$scope.$watch('users', function(users) {
console.log(users.length);
});
AS the angular code runs asynchronous so console.log($scope.users.length) runs before the promise returned.
var onUsers = function(response){
$scope.users = response.data;
console.log($scope.users.length); //here works
}
your console.log($scope.users.length) in above function works because it is a success handler.
you can get the your console.log works out side the success handler by using $timeout. i am assuming after 5000 your response will arive.
$timeout(function() {
console.log($scope.users.length);
}, 5000);
I'm calling the following service:
Get accounts details
First piece of code in my controller:
//resp is coming from an $http sync request, above the below request.
for (var i = 0; i < resp.rows; ++i) {
userService.getAccountsBalance($rootScope.userid, resp[i]['Acct_Number']).then(function(data){
console.log(data.bal) // shows two duplicate balances
});
}
In my service:
app.service('userService', function($rootScope, $q){
var deferred = $q.defer();
this.getAccountsBalance = function(userid, accountNum){
console.log(userid + " " + accountNum)
var req = <my $http request>
req.send().then(function(resp){
deferred.resolve(resp.responseJSON);
});
console.log(deferred.promise) //// prints two balances JSON objects with no duplicate
return deferred.promise;
}
});
My question is, I can see that two requests(with different parameter) has been executed in my service, and returning two different balances for two accounts. However, in my controller I get two duplicate results. I only get the last response twice.
I'm pretty sure it has something to do with promises, and I'm still new.
The problem seems to be you are returning the same promise for both requests,
app.service('userService', function($rootScope, $q){
this.getAccountsBalance = function(userid, accountNum){
var deferred = $q.defer();
Change it like above and it should work.
In order to loop through promises, you should use the $q service which provide the $.all method that allows you to resolve only when all promises has been resolved, and then get the data for each promise
When there are multiple promises are involved,if anyone promise get resolved all promise will get resolved with same value.
So you should use $q.all() to get result of all promises.
You can do in this way.
Inject $q Service.
var arrAllPromise = [];
for (var i = 0; i < resp.rows; ++i) {
arrAllPromise[i] = userService.getAccountsBalance($rootScope.userid, resp[i]['Acct_Number'])
}
$q.all(arrAllPromise).then(function(data) {
console.log(data) // shows two duplicate balances
});
It will give all promise data with which it is resolved.
Basically problem was you were having common $q object for promise.When your 1st promise gets resolved, the outer $q promiseis resolved by some response.
Then you had 2nd call for the same function, it would make that $http call but return old resolved promise. Because old $q object has fulfilled its promise already.
For solving the problem, you should have separate $q.defer() inside method. So that each time proper data will return by service method
You are creating a custom promise using $q,which is considered as bad pattern. As you have $http.get method is there in place, you can utilize their promise(each $http method promise, and we can chain them using .then)
Service
app.service('userService', function($rootScope, $q){
this.getAccountsBalance = function(userid, accountNum){
var req = <my $http request>
//utilize promise object returned by $http
req.send().then(function(resp){
//return data that sent to promise chain function
return resp.data;
});
}
});
$scope.iter = 0;
$scope.myArray.forEach(function () {
$http.get($scope.myArray[$scope.iter].URL)
.success(function (data) {
$scope.myArray2.push(data);
//$scope.myArray2[$scope.iter]=data
});
$scope.iter++;
})
The above code works but I want the results in myArray2 in the same order as it was called. I know that I cannot expect $scope.myArray2[$scope.iter]=data to work but that is what I need.
I looked at the angular documentation on promises but could not make out how to use it for the above.
You can put all promises from the get requests in an array and use $q.all() to create a promise that resolves when all underlying promises resolve. You can then iterate the responses in the order they were added to the requests array, and push each response's data into the array in order...
function controller ($scope, $q) {
// ...
var requests = [];
var $scope.myArray2 = [];
angular.forEach($scope.myArray, function (value) {
requests.push($http.get(value.URL));
});
$q.all(requests).then(function(results) {
angular.forEach(results, function(result) {
$scope.myArray2.push(result.data);
});
});
}
Dont understand what you are trying to achieve, but here is an example of simple deferred promises in a controller:
var firstDefer= $q.defer();
firstDefer.promise.then(function(thing){
// here you make the first request,
// only when the first request is completed
//the variable that you return will be filled and
//returned. The thing that you return in the first .then
// is the parameter that you receive in the second .then
return thingPlusRequestData;
}).then(function(thingPlusRequestData){
//second request
return thingPlusPlusRequestData;
}).then(function(thingPlusPlusRequestData){
//and so on...
});
firstDefer.resolve(thing);
//when you call .resolve it tries to "accomplish" the first promise
//you can pass something if you want as a parameter and it will be
// the first .then parameter.
Hope this helps u :D
You will normally NOT get the results in the order you called the $http.get(...) function. Mind that the success(...) function is called asynchronously, whenever the http response comes in, and the order of those responses is totaly unpredictable.
However you can work around this by waiting for all the responses to finish, and then sort them according to your criteria.
Here is the working fiddle: http://fiddle.jshell.net/3C8R3/3/
I am using some data which is from a RESTful service in multiple pages.
So I am using angular factories for that. So, I required to get the data once from the server, and everytime I am getting the data with that defined service. Just like a global variables. Here is the sample:
var myApp = angular.module('myservices', []);
myApp.factory('myService', function($http) {
$http({method:"GET", url:"/my/url"}).success(function(result){
return result;
});
});
In my controller I am using this service as:
function myFunction($scope, myService) {
$scope.data = myService;
console.log("data.name"+$scope.data.name);
}
Its working fine for me as per my requirements.
But the problem here is, when I reloaded in my webpage the service will gets called again and requests for server. If in between some other function executes which is dependent on the "defined service", It's giving the error like "something" is undefined. So I want to wait in my script till the service is loaded. How can I do that? Is there anyway do that in angularjs?
You should use promises for async operations where you don't know when it will be completed. A promise "represents an operation that hasn't completed yet, but is expected in the future." (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise)
An example implementation would be like:
myApp.factory('myService', function($http) {
var getData = function() {
// Angular $http() and then() both return promises themselves
return $http({method:"GET", url:"/my/url"}).then(function(result){
// What we return here is the data that will be accessible
// to us after the promise resolves
return result.data;
});
};
return { getData: getData };
});
function myFunction($scope, myService) {
var myDataPromise = myService.getData();
myDataPromise.then(function(result) {
// this is only run after getData() resolves
$scope.data = result;
console.log("data.name"+$scope.data.name);
});
}
Edit: Regarding Sujoys comment that
What do I need to do so that myFuction() call won't return till .then() function finishes execution.
function myFunction($scope, myService) {
var myDataPromise = myService.getData();
myDataPromise.then(function(result) {
$scope.data = result;
console.log("data.name"+$scope.data.name);
});
console.log("This will get printed before data.name inside then. And I don't want that.");
}
Well, let's suppose the call to getData() took 10 seconds to complete. If the function didn't return anything in that time, it would effectively become normal synchronous code and would hang the browser until it completed.
With the promise returning instantly though, the browser is free to continue on with other code in the meantime. Once the promise resolves/fails, the then() call is triggered. So it makes much more sense this way, even if it might make the flow of your code a bit more complex (complexity is a common problem of async/parallel programming in general after all!)
for people new to this you can also use a callback for example:
In your service:
.factory('DataHandler',function ($http){
var GetRandomArtists = function(data, callback){
$http.post(URL, data).success(function (response) {
callback(response);
});
}
})
In your controller:
DataHandler.GetRandomArtists(3, function(response){
$scope.data.random_artists = response;
});
I was having the same problem and none if these worked for me. Here is what did work though...
app.factory('myService', function($http) {
var data = function (value) {
return $http.get(value);
}
return { data: data }
});
and then the function that uses it is...
vm.search = function(value) {
var recieved_data = myService.data(value);
recieved_data.then(
function(fulfillment){
vm.tags = fulfillment.data;
}, function(){
console.log("Server did not send tag data.");
});
};
The service isn't that necessary but I think its a good practise for extensibility. Most of what you will need for one will for any other, especially when using APIs. Anyway I hope this was helpful.
FYI, this is using Angularfire so it may vary a bit for a different service or other use but should solve the same isse $http has. I had this same issue only solution that fit for me the best was to combine all services/factories into a single promise on the scope. On each route/view that needed these services/etc to be loaded I put any functions that require loaded data inside the controller function i.e. myfunct() and the main app.js on run after auth i put
myservice.$loaded().then(function() {$rootScope.myservice = myservice;});
and in the view I just did
ng-if="myservice" ng-init="somevar=myfunct()"
in the first/parent view element/wrapper so the controller can run everything inside
myfunct()
without worrying about async promises/order/queue issues. I hope that helps someone with the same issues I had.
I'm trying to setup a data service in my Angular application that is using breezeJS. After I resolve my promise I can't get the .then to file in my controller. I am getting data back from my database via breeze in my data service. I could just pass back the breeze promise but I want to be able to use $q.all to know when all my data has been found.
In my controller`
ProApp.controller('caseInfoController',
function caseInfoController($scope, $log, $timeout, caseDataService) {
/***initialize data ***/
// initializeApp();
ATPinitializeApp();
function ATPinitializeApp() {
$scope.MyStateList= caseDataService.getAllStates()
.then(function assignStates(data) {
$log.info("THIS THEN WILL NOT FIRE");
});
}
`
The above then will not fire when the promise from the data service is fulfilled.
ProApp.factory('caseDataService', function ($log, $q)
{
breeze.config.initializeAdapterInstance("modelLibrary", "backingStore", true);
var servicename = "http://localhost:60882/breeze/SPLBreeze";
var manager = new breeze.EntityManager(servicename);
var caseDataService =
{
getAllStates: getAllStates,
};
return caseDataService;
/*** implementation details ***/
function getAllStates()
{
var myStatePromise = $q.defer();
var query = breeze.EntityQuery
.from("state");
manager.executeQuery(query)
.then(function (data) {
$timeout(function () { myStatePromise.resolve(data); }, 200);;
});
return myStatePromise.promise;
};
Any help would be greatly appreciated. I'm not 100% sure if I have the $q promises set up correctly. Eventually I would like to use a $q.all to determine when an array of various promises have been resolved to so I can update a message to the user. I've been reading that I need to use a timeout to get angular to realize that a change has happened in the next event loop.
You're staring down the barrel of an Angular $q bug. You can read about it here if you wish.
That won't help nearly as much as following the advice in my answer to this related StackOverflow question in which I show and describe an adapter to get from Q.js promise to a $q promise.
I have never used breeze but I think your problem is that you are not returning anything on the success callback..
/***initialize data ***/
. . .
function ATPinitializeApp() {
$scope.MyStateList= caseDataService.getAllStates()
.then(function assignStates(data) {
$log.info("THIS THEN WILL NOT FIRE");
return data; // If you donĀ“t return anything nothing will be added to the scope.
});
}
Also the $timeout on the getAllStates function should not be necesary since angular resolves the promises asynchronously (it queues the resolution using $rootScope.$evalAsync)
function getAllStates()
{
. . .
manager.executeQuery(query)
.then(function (data) {
// I believe the $timeout that was in this function is not necessary
myStatePromise.resolve(data);
});
return myStatePromis
e.promise;
}
Hope this could help you a little bit.
Regards,
Carles