calling angularjs-service iteratively - angularjs

I have an array of ids and would like to iterate over them and pass them to a service to fetch some data. But I would like to only move to the next id after the processing of the previous id has finished. After all the data has been fetched I need to call a specific function.
My code (without the iteration) wold be something like
MyService.fetch(id)
.success(function (data, status, headers, config) {
doSomething();
});
What I want to achieve is something like this but in a way which can handle an unknown number of items in my array of ids:
MyService.fetch(id).success(function (data, status, headers, config)
{
MyService.fetch(id2).success(function (data, status, headers, config)
{
doSomething();
});
});
Any ideas how to achieve this ?
thanks
Thomas

Angular comes with a lite promise library: $q.
It's actually quite simple to do.
Service
myApp.factory('theProcessor', function($q, $timeout) {
return {
fetch: function(queue, results, defer) {
defer = defer || $q.defer();
var self = this;
// Continue fetching if we still have ids left
if(queue.length) {
var id = queue.shift();
// Replace this with your http call
$timeout(function() {
// Don't forget to call d.resolve, if you add logic here
// that decides not to continue the process loop.
self.fetch(queue, results, defer);
results.push({ id: id, value: Math.floor((Math.random()*100)+1) });
}, 500);
} else {
// We're done -- inform our caller
defer.resolve(results);
}
// Return the promise which we will resolve when we're done
return defer.promise;
},
};
});
See it in action at this plunker.

Try to use following approuch:
var idsArray= [], result = [];
/// ...After filling array
function nextIteration(index) {
MyService.fetch(idsArray[index]).success(function (data, status, headers, config)
{
result.push(data);
if (++index < idsArray.length) {
nextIteration(index)
} else {
console.log('Task complete');
}
}
nextIteration(0);

You could use the $q's all() method to bundle all the promises that you define and then do something after all of them are resolved e.g:
$q.all([promise1, promise2, ...]).then(...)
You may want to consider implementing this feature in your controller or your service.
Take a look at HERE for a complete API reference and details.
UPDATE
Just thinking that your service could accept an array of ids and it could have a method which would recursively fetch the data in order that you want. Look and the following code, it's an idea so it may not work as is:
function(){
var result = [];
var fetch = function(idArr /*this is your ID array*/){
(a simple guess if what you want to do with that ID)
$http.get('yourURL?id=' + <first element of idArr>)
.success(function(data){
//some logic
result.push(data);
idArr.splice(1,0);
fetch(idArr);
});
}
}

Related

Angular Promise Error

I am new to angularjs. Correct me if i am doing this wrong about promise.
doSomething(x)
{
console.log(x);
}
var something = 0;
$http.post('/url')
.then(function (response) {
something = response.data;
})
doSomething(something);
assuming that variable something has value..
everytime i log it in the console inside the doSomething method. the value
i receive is always 0;
what i did is put the method inside the then clause.
and i get the right value.
var something = 0;
$http.post('/url')
.then(function (response) {
something = response.data;
doSomething(something);
})
is this 2nd code right? or did i miss something in my first code?
thanks
Your second code is right. Because $http returns a promise and that can take n time to return so it never goes in the .then instead the next statement is triggered.
So, if suppose now you want to do something like your first code then you can use $q service. Like so (But, I won't recommend doing it here as it is not needed here).
doSomething(x)
{
console.log(x);
}
var defer = $q.defer();
$http.post('/url')
.then(function (response) {
defer.resolve(response.data);
},function (err) {
defer.reject(err);
});
defer.promise.then((data)=>{
doSomething(data);
});

Passing same parameter, multiple times in same query to Rest service using $resource

I am consuming this Rest web service from my AngularJS application and I'm using $resource to pass parameters and get my data.
I have a particular service that can take the same parameter multiple times like this in my Postman session:
/myService/Accounts?acctId[]=7029&acctId[]=3324
I've tried to pass a generated string to my $resource query methods placeholder but it ends up escaping all my brackets and not working.
I also tried not using a placeholder and passing the same parameter twice, it doesn't fail but it takes the last value so that does not work either.
$scope.resultsColl = rfpService.searchAccountName.query({
acctName: $scope.accountName,
acctRole: acctRoleParams,
acctRole: acctRoleParams2
});
Hopefully someone has had this same issue? Any ideas?
From the research I've done on other posts it looks like you can't dynamically generate using $resource. My working solution was to switch to $http, this way I could dynamically generate multiple acctRole[] params:
var acctRoleParams = '';
if(corporate === true)
{
acctRoleParams = '&acctRole[]=C';
};
if(smallBiz === true)
{
acctRoleParams += '&acctRole[]=B';
};
$http.get("https://myRestServiceUrl/services/customer/accounts?acct=" + $scope.account + acctRoleParams)
.success(function (data, status, headers, config) {
$scope.gridOptions.data = data.data.detailsByAcct;
})
.error(function (data, status, headers, config) {
showToast("An error occurred during the request");
})
.finally(function () {
$scope.isGridLoading = false;
});
I wanted to post this in case anyone else has been struggling to do the same thing.

How to Prime $http $cache with AngularJS

When a user logs into the main page of my site, I typically load quite a bit of data on the home page. much more than when they come to a specific url on the page. When they hit the ome page, that actually fullfills the data requests of much of the data that I grab individually when they hit a specific page.
I like how the $http module works with $cache and I'm wanting to use the knowledge of my home page hit to populate the cache of calls I know the individual page will make.
That is, as an example, my home page calls /rest/clients which returns all clients while individual pages call /rest/client/101. What I want to do is make it so that if /rest/clients is called first, then when /rest/client/101 is called an new fresh xhr call does not have to be made but the data can be gotten from the cache as if /rest/client/101 had already been called.
I've never done a decorator before but I'm thinking maybe a decorator on the $http service? I looked through the $http code and it seemed the cache is stored in closure to the actual http call and not exposed except on the next Get.
Has anyone done this or similar? I could not find it. Any specific pseudo coding suggestions would be very welcome.
In your data service you have 2 methods, getAll and getOne.
In the service define a reference to your getAll results promise.
Then in your getOne service check to see if that promise exists and if it does use it to filter out the one item that you need to satisfy your getOne need.
module.service('dataService', function($http){
var getAllPromise = null;
this.getAll = function(){
if (getAllPromise !== null){
getAllPromise;
}
getAllPromise = $http.get('clients');
return getAllPromise
};
this.getOne = function(id){
if (getAllPromise !== null){
return getAllPromise
.then(function(allData){
//logic here to find the one in the full result set
return theFoundItem;
};
}
return $http.get('clients/' + id);
};
});
I found the solution I asked for but implementing and making it testable is proving to be beyond my skills. I'm going to go with #brocco solution but for the permanent record I'm leaving the actual answer to what I was asking. I'm not marking this as the correct solution because #brocco solution is better for my real problem. So, thank you #brocco for the help.
You can see below what I'm basically doing is to create my own $cache with $cacheFactory. I then use the .put method of the new cache object to prime my cache. Then, subsequent calls to the client/1 url will get the cache'd record without ever having to call cache/1 in real live. The cache is loaded in the for loop from the first big call.
Thanks for everyones input on this.
var myApp = angular.module('myApp', []);
myApp.factory('speakersCache', function($cacheFactory) {
return $cacheFactory('speakersCacheData');
});
myApp.controller('personController', ['$scope','$http','speakersCache', function ($scope,$http,speakersCache) {
$scope.getAllSpeakers = function() {
$http.get('speakers.json',{cache: speakersCache}).
success(function (data, status, headers, config) {
debugger;
var i;
for(i=0;i<data.length;i++) {
var url = 'speaker/' + i;
speakersCache.put(url, data[i]);
}
}).
error(function (data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
};
$scope.getAllSessions = function() {
$http.get('sessions.json',{cache: speakersCache}).
success(function (data, status, headers, config) {
debugger;
}).
error(function (data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
};
$scope.getOneSpeaker = function() {
$http.get('speaker/1',{cache: speakersCache}).
success(function (data, status, headers, config) {
debugger;
}).
error(function (data, status, headers, config) {
debugger;
});
}
$scope.checkit = function() {
var x = speakersCache;
debugger;
};
}]);
If I understand you well, I have done something similar:
I have this code:
.factory('myOwnEntity', ['$filter',
function ($filter) {
var myOwnList = [];
return {
set : function (data) {
myOwnList = data;
},
get : function () {
return myOwnList;
},
find : function (id) {
return $filter('filter')(myOwnList, { itemId : id }).pop();
}
}
}
])
When I make the petition to the Web Service, I store the information like this:
$http.get(url, {
cache : true
})
.success(function (data) {
myOwnEntity.set(data);
defer.resolve(data);
});
return defer.promise;
Now, the next time I need some information, I just query my entity with the find method. Hope this is what you are looking for.

AngularJS - why promises ($q) with $http?

I am learning AngularJS after converting from jQuery for a few years. And some bits are much more intuitive. Some not so much :).
I am trying to get my head around the use of promises, particularly $q in use with $http and there does not seem to be too much information around these two combined that I can find.
Why would I use promises in place of the success/error callback? They both make uses of callbacks in reality, so why is a promise considered better? E.g. I could set up a get(...) function like follows:
function get(url, success, error) {
success = success || function () {};
error = error || function () {};
$http.get(url)
.success(function (data) {
success(data);
})
.error(function (error) {
error(error);
});
}
get('http://myservice.com/JSON/',
function () {
// do something with data
},
function () {
// display an error
}
);
Which is good(?) because it gives me complete control over what is happening. If I call get(...) then I can control any success/errors wherever get is called.
If I convert this to use promises, then I get:
function get(url) {
return $http.get(url)
.then(function (data) {
return data;
},
function (error) {
return error;
});
}
get('http://myservice.com/JSON/')
.then(function (data) {
// do something with data
});
// cannot handle my errors?
Which is condensed, I agree; we also do not have to explicitly worry about the success/error callback, but I seem to have lost control over my error callback for a start - because I cannot configure a second callback to handle an error.
Which means that if I use this function in a service which can be used by multiple controllers, then I cannot update the UI to alert the user to an error.
Am I missing something? Is there a reason why promises is preferred? I cannot find an example why.
Usually you'll deal with asynchronous tasks in Javascript with callbacks;
$.get('path/to/data', function(data) {
console.log(data);
});
It works fine, but start to complicate when you go into whats called the 'callback hell';
$.get('path/to/data', function(data) {
$.get('path/to/data2' + data, function(data2) {
$.get('path/to/data3' + data2, function(data3) {
manipulate(data, data2, data3);
}, errorCb);
}, errorCb);
}, errorCb);
The alternative is working with promises and defered object;
Deferreds - representing units of work
Promises - representing data from those Deferreds
Sticking to this agenda can assist to you in every extreme asynctask case:
You have a regular call that need to get data from the server, manipulate it, and return to the scope
You have multiple calls that each is depending on the precious one (cahin strategy)
You want to send multiple (parallel) calls and handle their success in 1 block
You want your code to be orginized (prevent dealing with handling results on controllers)
Your task is the easiest one to handle with $q and $http
function get(url) {
var deferred = $q.defer();
$http.get(url)
.success(function (data) {
deferred.resolve(data);
})
.error(function (error) {
deferred.reject(error);
});
return deferred.promise;
}
And calling the service function is the same
get('http://myservice.com/JSON/')
.then(function (data) {
// do something with data
});
// cannot handle my errors?
You can handle the error like this:
get('http://myservice.com/JSON/')
.then(function (data) {
// do something with data
},
function (error) {
//do something with error
});
But unfortunately since you have already caught the error then the final error won't be triggered. You will also have the same problem with success.
To get that to work you ned to use $q.
function get(url) {
var deferred = $q.defer();
$http.get(url)
.success(function (data) {
deferred.resolve(data);
})
.error(function (error) {
deferred.reject(error);
});
return deferred.promise;
}
Also there is no need to pass in success and error functions because you can use promises instead.

Exception handling with jsonp requests in angularjs

I have the following code which make seperate requests for jsonp data.
In the code "doRequestA" works fine and returns a result. The issue I have is
I need to catch any errors if they occur. I have tried implementing this in
"doRequestB", but only receive the alert error (I have ommitted the callback from doRequestB).
Here is the fiddle http://jsfiddle.net/a4Rc2/417/
function jsonp_callback(data) {
alert(data.found);
}
function jsonp_example($scope, $http) {
$scope.doRequestA = function () {
var url = "http://public-api.wordpress.com/rest/v1/sites/wtmpeachtest.wordpress.com/posts?callback=jsonp_callback";
$http.jsonp(url);
};
$scope.doRequestB = function () {
var url = "http://public-api.wordpress.com/rest/v1/sites/wtmpeachtest.wordpress.com/posts";
$http.jsonp(url)
.success(function (data) {
alert(data.found);
}).error(function (data, status, headers, config) {
alert('error');
});
};
}
Any advice is greatly appreciated, thanks in advance.
You actually are using $http.jsonp incorrectly in both cases. You just can't see the error in the first case because you are not handling it.
With Angular.js's $http.jsonp method, the callback method is handled automatically. You shouldn't use your own methods in the result string, but rather insert JSON_CALLBACK (exactly as written) into your string. This way, you can handle the response using the promise returned from Angular. If you watch the network activity (say, using Firebug or the developer tools in your browser of choice), you'll see JSON_CALLBACK replaced with something like angular.callbacks._0*.
In the second example, you don't have a callback method defined at all, so the result will always error. There's actually no way for the jsonp result to be handled, since it simply returns the JSON object without a callback method, and the result just is ignored.
Here's a working result: http://jsfiddle.net/tPLaN/1/
The code:
function jsonp_callback(data) {
alert(data.found);
}
function jsonp_example($scope, $http) {
$scope.doRequestA = function() {
var url = "http://public-api.wordpress.com/rest/v1/sites/wtmpeachtest.wordpress.com/posts?callback=JSON_CALLBACK";
$http.jsonp(url).success(function(data) {
jsonp_callback(data);
});
};
$scope.doRequestB = function() {
var url = "http://public-api.wordpress.com/rest/v1/sites/wtmpeachtest.wordpress.com/posts?callback=JSON_CALLBACK";
$http.jsonp(url)
.success(function (data) {
alert(data.found);
}).error(function (data, status, headers, config) {
alert('error');
});
};
}
The only thing I changed was
Correcting the two URLs.
Moving the callback handler on the first method inside the .success() method on the promise.
Believe it or not, the need for JSON_CALLBACK is in the documentation for $http.jsonp, but it's sort of hidden.
* Note, please do not use the replacement for JSON_CALLBACK for anything. It's a private method generated by Angular, I am just showing it to help make more sense of what is happening.

Resources