Loading of paginated data from API - angularjs

I am working on a system that currently requires me to load all items from an API.
The API is built with pagination feature in it. I keep calling the API a number of times and $http.get cursing the system not to respond. For example, once I load the page that needs to call the API many times (like 50 to 80 times depending on the number of pages), for a few minutes anything I do won't respond until the calling of the API is almost finished. I already tried a lot of ways but it won't work.
$scope.loadAllPagedItems = function (category_uuid, max_pageitem, item_perpage) {
for (var a = 0 ; a < max_pageitem; a++) {
itemResource.findItems(category_uuid, item_perpage, a).then(function (response) {
if (response.data.data.length > 0) {
for (var a = 2 ; a < $scope.categories.length; a++) {
if ($scope.categories[a][0].category_uuid == response.data.data[0].category_uuid) {
for (var j = 0; j < response.data.data.length; j++) {
$scope.categories[a][0].data.push(response.data.data[j]);
}
}
}
}
}, function (error) {
console.log(error);
})
}
}
Is there any way I can do this better?

Sequentially Retrieving Paginated Data from an Asynchronous API
$scope.loadAllPagedItems = function(category_uuid, max_pageitem, item_perpage) {
var promise = $q.when([]);
for (let a = 0 ; a < max_pageitem; a++) {
promise = promise
.then(function(dataArray) {
var p = itemResource.findItems(category_uuid, item_perpage, a);
p = p.then(function (response) {
return dataArray.concat(response.data.data);
});
return p;
});
};
return promise;
};
The above example, executes XHRs sequentially and uses the array concat method to merge the resolved arrays into a single array.
Usage:
$scope.loadAllPagedItems(category, pages, itemsPerPages)
.then(function(finalArray) {
console.log(finalArray);
}).catch(function(response) {
console.log("ERROR ",response.status);
throw response;
});

Related

iterating inside a $http.get

I've this block of code which displays 20 items per request.
.controller('ActorController',function($scope,$http) {
$scope.getActors = function () {
var actors = $http.get(https://api.themoviedb.org/3/person/popular&page=1);
actors.then(
function (response) {
$scope.actors = response.data.results;
}
);
}
})
Instead of page=1, if i put page=2, I'll get another set of 20 items and so on. How to iterate inside the $http.get if I want more than 1 get request in a single page? I want to display the 2nd 20 items after displaying the 1st 20.
So you're saying that you want to make multiple calls to $http.get in a loop? If so you'd do something like this:
.controller('ActorController',function($scope,$http) {
$scope.getActors = function () {
for(var i = 0; i < 5; i++) {
getPage(i);
}
}
function getPage(i) {
var actors = $http.get('https://api.themoviedb.org/3/person/popular&page=' + i);
actors.then(function (response) {
for(var j = 0; j < response.data.results.length; j++) {
$scope.actors.push(response.data.results[j]);
}
});
}
})
That will put the data for the first 5 pages into the $scope.actors array.
you can use $q.all to send multiple http requests at once
var fulAr = [];
for (var i = 1; i <= 2; i++) {
fulAr.push($http.get("https://api.themoviedb.org/3/person/popular&page=" + i))
}
$q.all(fulAr).then(function(response) {
console.log(response[0].data) //page 1 content
console.log(response[1].data) //page 2 content
for(var j = 0; j < response.length; j++) {
$scope.actors = $scope.actors.concat(response[j].data.results);
}
})
Make sure to inject $q to controller

Iterating inside $http.get

I've this block of code which displays 20 items per request.
.controller('MovieController',function ($scope,$http) {
$scope.movies = $http.get(https://api.themoviedb.org/3/movie/popular&page=1)
.then(
function (response) {
$scope.movies = response.data.results;
}
);
})
Instead of page=1, if i put page=2, I'll get another set of 20 items and so on. How to iterate inside the $http.get if I want more than 1 get request in a single page? I want to display the 2nd 20 items after displaying the 1st 20.
There is the classic way of chaining callback functions together to achieve the desired result. Or there is the better way, which uses $q.all().You should use $q.all to send multiple http requests at once
var reqAr = [];
for (var i = 0; i <= 1; i++) {
reqAr.push($http.get("https://api.themoviedb.org/3/movie/popular&page=" + i))
}
$q.all(reqAr).then(function(response) {
console.log(response[0].data) //page 1 content
console.log(response[1].data) //page 2 content
for(var j = 0; j < response.length; j++) {
$scope.movies = $scope.movies.concat(response[j].data.results);
}
})

Javascript Application is crashing every 4-5 hrs in chrome and firefox

My application is crashing every 4-5 hrs and I am not sure it is happening because of memory leak or any code performance issue.
I have 3-4 functions which have timeouts to call self every 5 sec and they does some data massaging before painting the DOM.
As you can see my snapshots keep on increasing every time I take new snapshot.
Unable to find what exactly is happening. But if we look at the snapshots we can say that the strings has been increasing significantly from snapshot 1 to snapshot 9.
Any help is appreciated.
Sample code:
function fetchSummary() {
if(!$rootScope.pageVisible) {
$scope.fetchSummaryTimeout = $timeout(fetchSummary, 1000);
$scope.lastRefreshedDateTime = new Date();
return;
}
$http
.get(apiUri + '/statusboard/api/summaryDb')
.success(function (response) {
if (response && response.length) {
$scope.databases = response[0];
$scope.fetchSummaryTimeout = $timeout(fetchSummary, 5000);
var i, j, l;
//alert counts
angular.forEach($scope.databases.summary, function (summary) {
if (summary.eventType === "database_connection_alert") {
$scope.connectionCount += summary.count;
}
if (summary.eventType === "database_session_alert" || summary.eventType === "database_load_alert") {
$scope.warnCount += summary.count;
}
});
for (l = 0; l < $scope.databases.components.length; l++) {
var component = $scope.databases.components[l];
//some data stuff
for (j = 0; j < component.summary.length; j++) {
var summary = component.summary[j];
}}
$scope.coloCounts.any = $scope.coloCounts.All + $scope.coloCounts.lvs + $scope.coloCounts.slc + $scope.coloCounts.phx;
for (i = 0; i < $scope.databases.vcsStatus.length; i++) {
//Some stuff
}
}
} else {
$scope.fetchSummaryTimeout = $timeout(fetchSummary, 5000);
}
angular.element("#last-updated, #last-updated-overlay").remove();
})
.error(function () {
$scope.fetchSummaryTimeout = $timeout(fetchSummary, 5000);
});
}
}
"$scope.databases" is the variable every time i set the response to.

Prevent multiple submits in angularjs

I'm looking for a AngularJS-based way to prevent multiple submits per task.
I don't need buttons to be disabled after submission or close the form and wait for the task to be completed. Instead, I need requests to be unique.
To be more detailed, I need $http.get and $http.post stop sending multiple same requests.
Any Ideas?
According to this article, you can use provider decorator.
NOTE: this approach is based on angular-api
https://gist.github.com/adambuczynski/354364e2a58786e2be71
UPDATE
I've changed a little part in your suggested solution, because returned promises have lost .success and .error and .then.
Just use this edited code to have all of those functions working:
.config(["$provide", function ($provide) {
$provide.decorator('$http', function ($delegate, $q) {
var pendingRequests = {};
var $http = $delegate;
function hash(str) {
var h = 0;
var strlen = str.length;
if (strlen === 0) {
return h;
}
for (var i = 0, n; i < strlen; ++i) {
n = str.charCodeAt(i);
h = ((h << 5) - h) + n;
h = h & h;
}
return h >>> 0;
}
function getRequestIdentifier(config) {
var str = config.method + config.url;
if (config.data && typeof config.data === 'object') {
str += angular.toJson(config.data);
}
return hash(str);
}
var $duplicateRequestsFilter = function (config) {
if (config.ignoreDuplicateRequest) {
return $http(config);
}
var identifier = getRequestIdentifier(config);
if (pendingRequests[identifier]) {
if (config.rejectDuplicateRequest) {
return $q.reject({
data: '',
headers: {},
status: config.rejectDuplicateStatusCode || 400,
config: config
});
}
return pendingRequests[identifier];
}
pendingRequests[identifier] = $http(config);
$http(config).finally(function () {
delete pendingRequests[identifier];
});
return pendingRequests[identifier];
};
Object.keys($http).filter(function (key) {
return (typeof $http[key] === 'function');
}).forEach(function (key) {
$duplicateRequestsFilter[key] = $http[key];
});
return $duplicateRequestsFilter;
})
}])
It could be a performance issue but following idea could solve your problem.
Store the each request URL and DATA as key value pair on a variable. URL should be KEY. For Same URL multiple submission can be stored in a Array.
Then for any new call check the URL if it present in your stored object, then compare the data with each object thorughly (deep check, it is costly though).
If any exact match found then stop the processing. As same request came.
Other wise proceed and don't forget to store this data also.
But it is costly since need to check the data which could be havy.
Note: At the time of storing the data you could convert it to JSON String so it will be easier to compare between String.
here is the Code Algo
YourService.call(url, params) {
var Str1 = JSON.stringify(params);
if(StoredObj[url]) {
for each (StoredObj[url] as Str){
if(Str === Str1) {
return;
}
}
}
else {
StoredObj[url] = []; //new Array
}
StoredObj[url].push(Str1);
Call $http then;
}

How to do paginated API calls until reaching the end?

I imagine this could be a pretty general problem, but in this case I'm using AngularJS and the SoundCloud API.
Here's the flow:
Call loadTracks()
loadTracks() should load the tracks of a SoundCloud user, 50 at a time, until the list runs out.
loadTracks() does this by calling another function, sc.getTracksByUser(id), which returns a promise
loadTracks() should update the variable $scope.tracks with each 50 track batch when it arrives
The SoundCloud API provides an option offset, so loading the batches is relatively easy. I think it's the promise that is tripping me up. Without the promise, the solution would be:
$scope.tracks = [];
var loadTracks = function() {
var page = -1,
done = false,
newTracks;
while (!done) {
newTracks = getFiftyTracks(page++);
for (var i = 0; i < newTracks.length; i++) {
$scope.tracks.push(newTracks[i]);
}
if (newTracks.length < 50) done = true;
}
}
Unfortunately, that line with getFiftyTracks in it is not how it works. The actual implementation (using a promise) is:
sc.getTracksByUser(id).then(function (response) {
for (var i = 0; i < response.length; i++) {
$scope.tracks.push(response[i]);
}
}
I'm guessing the solution to this is some sort of recursion, but I'm not sure.
You can do that in this way
sc.getTracksByUser(id).then(function (response) {
for (var i = 0; i < response.length; i++) {
$scope.tracks.push(response[i]);
}
// if response return 50 track call getTracksByUser again
if (response.length === 50) sc.getTracksByUser(id);
});

Resources