aws partner logo
ibm partner logo
googlecloud partner logo
microsoft partner logo
Partners
Blogs

Learn how to implement Infinite Scroll Pagination in AngularJS

01/Apr/2014 Posted By Barkat Dhillon 3 comments.
Tags ,

I bet you have seen and pretty used to the old fashioned pagination that looks something like below:old-paginationThe pagination style above suffers from a significant UX issues. The user has to guess on what page is the result he is looking for. He clicks and clicks in the hope of finding the right page and finally gets frustrated and leaves your site. Also, you are asking too many inputs from user to traverse between the pages. Previous, Next, Page Numbers .. turns out though this is something that users have really gotten used to. As a developer too,  such a pagination scheme is very easy to implement as it loads limited data .How will you react if I improve user's  experience by changing multiple pages to a single page, user does not need to click between pages because all the records are being shown in single page now & I guess that makes him happy! At the same time you don't need to return entire data set at once which means you can still send same chunk of data records as you were sending earlier, but bonus point here is that you need to send data again which you sent earlier, i.e. if you have already sent records 11-20 and user wants to see those again, request will not come to server, client will handle it internally.

Let's do the magic

Let's first discuss our design approach to achieve such functionality where we can show paginated records but in same page. Please refer following diagram:Design ApproachIn above diagram you can refer first column where browser displays only few records i.e. first page as in older pagination approach. We will observe scroll down event by the user and when we find out that user has reached bottom of record list then we will initiate an AJAX request to get next page record, do not replace current page's record but append to current record list. Now user will have a experience that he is scrolling down same page, but you are sending data in chunks with the help of AJAX request.
I am going to implement this design in AngularJS, but you can implement this as per your framework as well. I will take an example of Courses where application is showing list of courses.

Back-end Service Contract

You need to define service method which can provide you data accordingly. We need 2 method as follows:
  1. getCourseCount() and it will get me total no. of courses exists in system so that application can stop loading more once it reaches count limit. url: /v1/courses?count
  2. getCourses(offset:Number, limit:Number) and it return list of records start from offset and upto limit records. Url: /v1/courses/offset/:offset/limit/:limit

Directive to Load Records on Scroll Down

Now application needs a directive which you can reuse throughout the application for all the pagination. This directive should be generic enough and should take a callback method as parameter so that each page can call its specific logic to load next chunk of records.
angular.module('app.directives.pvScroll', [])
.directive('pvScrolled', function() {
return function(scope, elm, attr) {
var raw = elm[0];
elm.bind('scroll', function() {
if (raw.scrollTop + raw.offsetHeight >= raw.scrollHeight) {
scope.$apply(attr.pvScrolled);
}
});
};
});
Above directive will bind an event 'scroll' to current element and as soon it reaches bottom of current element, it will invoke function passed in as parameter while applying this directive on an element.

Template to Render List

Use above coded directive in your view template so that it bind 'scroll' event with your container. Container could be anything either your full browser window or just a fraction of page e.g. a div showing list of records, in this case you need to define a height of container so that whenever scroll reaches that height end it should fire an event and load more records. Apply following CSS to your container.
.courses-list{
position : relative;
width    : 100%;
height   : 500px;
overflow : hidden;
}
Assign above defined CSS class to container and bind pvScrolled directive to it as well as follows:
<div>
<div>
<span>
Showing {{resultList.length}} out of {{total}} records
</span>
</div>
<div>
<div class="courses-list"  pv-scrolled="loadMoreCourses()">
<div ng-class-odd="'course-box'" ng-class-even="'course-box-bgcolor'" ng-repeat="item in resultList">
<div>
<!-- render each course row here -->
</div>
</div>
<!-- This div will be showed whenever ajax request is active to bring more records -->
<div ng-show="loadingResult" class="span9 loading-inline alert alert-info">
</div>
<!-- if server does not has more records to load then following div will inform the user that it is end of records -->
<div ng-show="!loadingResult && (resultList.length == total)" class="span9 pv-message">
<legend>
No more results to display.
</legend>
</div>
<!-- if no records found then following div will be showed -->
<div ng-show="!loadingResult && resultList.length == 0" class="span9 pv-message">
<legend>
No record found!!!
</legend>
</div>
</div>
</div>
</div>

Controller to Load Records on scroll down

In Controller we need to
angular.module('app.search.courses', [
'ui.state',
'app.services.course.courseService'
])
.config(['$stateProvider', function (stateProvider) {
stateProvider
.state('courses', {
url: '/courses',
views: {
'headerView@': {
templateUrl: 'templates/header.tpl.html'
},
'': {
templateUrl: 'courses/templates/courses.tpl.html',
controller: 'CourseCtrl'
},
'footerView@': {
templateUrl: 'templates/footer.tpl.html'
}
}
});
}])
.controller('CourseCtrl', ['$scope', 'CourseService',
function (scope, CourseService) {
scope.pagination = {
noOfPages: 1,
currentPage: 0,
pageSize: 10
};
scope.resultList = [];
scope.loadMoreCourses = function () {
if (scope.loadingResult) {
return;
}
if (scope.pagination.currentPage >= scope.pagination.noOfPages) {
return;
}
scope.pagination.currentPage = scope.pagination.currentPage + 1;
scope.offset = (scope.pagination.currentPage - 1) * scope.pagination.pageSize;
scope.limit = scope.pagination.pageSize;
scope.loadingResult = true;
scope.resultList = CourseService.courses.list({offset:scope.offset, limit:scope.limit});
scope.loadingResult = false;
};
scope.initializeResultList = function () {
CourseService.courses.count({}, function (count) {
scope.total = count;
scope.pagination.noOfPages = Math.ceil(count / scope.pagination.pageSize);
scope.loadMoreCourses();
});
}
scope.initializeResultList();
}]);
In the controller, we have declared a scope.pagination which captures pageSize i.e. limit or chuck size we are loading for every request, noOfPages i.e. total no. of requests required to traverse whole list and last is currentPage i.e. last accessed page so that if new request comes in to load more it will calculate which next chunk required to be loaded. There are 2 methods defined too, loadMoreCourses() which will be called every time scroll reached bottom of container or when page loaded first time.This method has check that if method has already initiated load more AJAXrequest, it will not initiate next one. Other is initializeResultList(), which will called one time at the time loading of controller and it will set total no of records and loads first chunk of records. In html code, we have declared 3 different divs other than result list:
  1. Loading div: it will be shown whenever a AJAX request to load more records is in-progress.loadingMore
  2. No More Records to load: it will be shown whenever user has reached end of all the records available in the system. It means application will not load more records.noMoreRecords
  3. No Record found: If there is no record to show at all, then it will be show.noRecord
Please refer the similar implementation like we discussed aboveProversity - All Courses

Conclusion

By now you should be able to use this function in any of your AngularJS application or you can implement this design in your specific framework as well, if you have any additional suggestions or questions regarding this blog, feel free to leave a comment. Would love to share any ideas with you guys.by Barkat Dhillon

Category : App Development