How to use angular 1.6.5 call $http request recursively when data set returned is limited

359 Views Asked by At

I would like to use Angular 1.6.5 for a project rebuild, but I'm not sure how to use the $http.get request in a factory when the source returns only a limited number of records at a time (1000 returned per request) and there are over 2000 records that I need to get.

In my current code I use jquery ajax and in the .done method I check for the presence of the value "__next", and if it exists, I recall the function passing the value "__next". When the "__next" value isn't returned, I do something with the data.

function getSpecifiedList(url){

    var specUrl = url;

    $.ajax({
        url: specUrl,
        type: "GET",
        headers:{"accept":"application/json;odata=verbose",
          error: function(xhr){
            console.log(xhr.status + " " + xhr.statusText);
          }
        }
    }).done(function (results){
        $("#wc_report_holder").text(results.length);

        //buildObjects processes the results and adds to an array
        buildObject(results);
        if(results.d.__next){
            getSpecifiedList(results.d.__next);
        }else{
            buildGridView();
        }
    }).fail(function(error){
        $("#wc_report_holder").text("There was an error: " + error);
    });
}

I would like to figure out how to implement that same value check and recursive call in angular 1.6.5 using best practice and most efficient but I haven't had luck figuring it out based on the angular docs and Googling.

Here is a short version of what I currently have using Angular 1.6.5.

<script>
var sitesApp = angular.module("sitesApp", ['ngRoute']);

sitesApp.controller('SitesListCtrl', ['$scope', 'sites',
    function ($scope, sites) {
        sites.list().then(function (response) {
            $scope.sites = response.data.value;
        });
    }
]);

sitesApp.controller("SiteDetailsCtrl", ['$scope', '$routeParams', 'sites',
    function ($scope, $routeParams, sites) {
        sites.find($routeParams.SiteCodePc, function (site) {
            $scope.site = site;
        });
    }
]);


sitesApp.config(function ($routeProvider, $locationProvider) {
    $locationProvider.hashPrefix('!');
    $routeProvider.
        when('/', {
            templateUrl: 'https://machine/sites/site-list.html',
            controller: 'SitesListCtrl'
        }).
        when('/:SiteCodePc', {
            templateUrl: 'https://machine/sites/site-details.html',
            controller: 'SiteDetailsCtrl'
        }).
        otherwise({
            redirectTo: '/'
        });
});

sitesApp.factory('sites', ['$http', function ($http) {
    var urlBase = "https://some-endpoint-for-data";
    var cachedData;

    function getData(callback) {
        if (cachedData) {
            callback(cachedData);
        } else {
            return $http({
                method: 'GET',
                url: urlBase
            })
            .then(function (response) {
                //HERE IS WHERE I THINK THE SOLUTION NEEDS TO BE IMPLEMENTED
                cachedData = response;
                return cachedData;
            });
        }
    }

    return {
        list: getData,
        find: function (SiteCodePc, callback) {
            getData(function (response) {
                var site = response.data.value.filter(function (entry) {
                    //debugger;
                    return entry.SiteCodePc === SiteCodePc;
                });
                callback(site[0]);
            });
        }
    };
}]);

</script> 

<div ng-app="sitesApp">
    <div ng-view></div>
</div>

Thanks in advance

2

There are 2 best solutions below

0
On

It looks like you can do a simple recursion where you accept a second (optional) parameter. If you are calling getData() for the first time then you can get your first 1000 results. However if you find __next then you will call it again sending the current 1000 results you have and concat the next 1000 results with the previous 1000.

sitesApp.factory('sites', ['$http', function ($http) {
var urlBase = "https://some-endpoint-for-data";

function getData(callback, results) {
    return $http({
        method: 'GET',
        url: urlBase
    })
    .then(function (response) {
        // If you have found a previous batch of results then concat the two arrays
        if(results) {
            response = response.concat(results);
        }
        // If there are more results to be found then recursively call the same function passing the batched results
        if(response.__next) {
            return getData(callback, response);
        }
        // If there are no more results to be found then trigger your callback function
        else {
            callback(response);
        }
    });
}

return {
    list: getData,
    find: function (SiteCodePc, callback) {
        getData(function (response) {
            var site = response.data.value.filter(function (entry) {
                //debugger;
                return entry.SiteCodePc === SiteCodePc;
            });
            callback(site[0]);
        });
    }
 };
}]);
0
On

I have implemented same kind of scenario with pagination logic and $q. In this sample code I am pulling the records recursively as lazy based on the LazyloadingLimit. You can specify the limit based on your requirement.So it only pulls the records based on the count from the total collection. In this below sample I am not using $http. On your real sample you can use the $http to pull the records from the server. Here I just hard coded the collection initially.

In your case you have to fetch total records count initially and apply some pagination logic or some other parameter to pull the next records.

angular.module('app', []);

angular.module('app').controller('SampleController', function ($scope,$http, $timeout, $q) {

    // $scope.initialize();
    $scope.mainCount = 0;
    $scope.lazyloadingLimit = 2;
    $scope.tileDefinitions = null;
    $scope.tempList = null;
    $scope.totalRecordCollection = [
        { "Name": "Record1" },
        { "Name": "Record2" },
        { "Name": "Record3" },
        { "Name": "Record4" },
        { "Name": "Record5" },
        { "Name": "Record6" },
        { "Name": "Record7" },


    ];
    function getTotalRecordCollection() {
        var deferred = $q.defer();
        deferred.resolve($scope.totalRecordCollection);
        return deferred.promise;
    }
    $scope.initialize = function () {
        debugger;
        var currentCount=0;
        var pageList = new Array();
        var currentPage = 1;
        var numberPerPage = 2;
        var numberOfPages = 0;
        function makeList() {
            numberOfPages = getNumberOfPages();
        }
        function getNumberOfPages() {
            return Math.ceil($scope.tempList.length / numberPerPage);
        }
        function nextPage() {
            currentPage += 1;
        } 
        function loadList() {
            var deferred = $q.defer();
            if (currentCount !== $scope.tempList.length) {

                var begin = ((currentPage - 1) * numberPerPage);
                var end = begin + numberPerPage;
                pageList = $scope.tempList.slice(begin, end);
                currentCount = currentCount + pageList.length;
                $scope.mainCount = currentCount;
                deferred.resolve(true);

            } else {
                debugger;
              return $q.reject();
            }
            return deferred.promise;

        }
        function loadNextRecords() {
            loadList().then(function (res) {
                nextPage();
                loadNextRecords();
            });
        }
        getTotalRecordCollection().then(function (response) {
            debugger;
            $scope.tempList = response;
            makeList();
            loadNextRecords();

        });

    }

});

 <body ng-controller="SampleController">
<input type="button" value="Click Here" ng-click="initialize()"/>
    {{mainCount}}
</body>

Once all the records are loaded , you should reject the promise else the recursive loops never end.

Hope this helps