LIDOR SYSTEMS

Advanced User Interface Controls and Components

Server Side Pagination in AngularJS Grid

Created: 07 March 2016

When you need to load large data sets into the AngularJS Grid, it is always a good practice to divide the data in multiple pages. On request, you can load new data into the Grid. In following sections of this article, we will show you how to create server side pagination in Grid directive.


Grid directive is part of IntegralUI Studio for Web
a suite of UI Components for development of web apps

In above demo, we have a grid with initial data page loaded. The pagination buttons below the grid, allows you to load data on demand, by requesting a new page data. Once the data is received from a remote data source, the grid will show it.

Server Side Pagination

In order to implement server side pagination, we need to know the number of rows per page. In our example, we will set this number to 10. That means whenever a new data page is requested, only 10 rows will be retrieved from the remote data source.

angular

.module("appModule", ["integralui"])

.controller("appCtrl", ["$scope", "IntegralUIGridService", "$timeout", function($scope, $gridService, $timeout){

$scope.gridName = "gridSample";

$scope.columns = [];

$scope.rows = [];

 

$scope.numRowsPerPage = 10;

 

$scope.columns = [

{ id: 1, headerText: 'Column 1' },

{ id: 2, headerText: 'Column 2' },

{ id: 3, headerText: 'Column 3' },

{ id: 4, headerText: 'Column 4' },

{ id: 5, headerText: 'Column 5' },

{ id: 6, headerText: 'Column 6' },

{ id: 7, headerText: 'Column 7' }

];

 

var initTimer = $timeout(function(){

loadNewPage();

 

$timeout.cancel(initTimer);

}, 1000);

 

$scope.onLoadComplete = function(){

$gridService.endLoad($scope.gridName);

}

}]);

<!DOCTYPE html>

<html>

<head>

<link rel="stylesheet" href="css/integralui.css" />

<link rel="stylesheet" href="css/integralui.grid.css" />

<link rel="stylesheet" href="css/themes/theme-flat-blue.css" />

<script type="text/javascript" src="external/angular.min.js"></script>

<script type="text/javascript" src="js/angular.integralui.min.js"></script>

<script type="text/javascript" src="js/angular.integralui.lists.min.js"></script>

<script type="text/javascript" src="js/angular.integralui.grid.min.js"></script>

</head>

<body>

<div ng-app="appModule" ng-controller="appCtrl">

<iui-grid name="{{gridName}}" columns="columns" rows="rows" paging="{ enabled: true, showControlPanel: false, pageSize: numRowsPerPage }" load-complete="onLoadComplete()" allow-focus="false"></iui-grid>

</div>

</body>

</html>

For pagination to work, the Grid directive must have its paging property set, with following values:

  • enabled - set to true so that Grid knows that data is divided in multiple pages
  • pageSize - set to 10, represents the number of rows per page
  • showControlPanel - when false, default pagination control panel is hidden

Now we have Grid directive ready to receive some data.

Create Custom Navigation Buttons

From above code, you may notice that we have showControlPanel set to false, this hides the default pagination controls. We will create a custom control panel, where buttons with page numbers are created on demand, while other buttons appear and disappear as requested.

For our pagination control panel, we are using a list of buttons. We are cycling through the list using ngRepeat directive where specific buttons are shown, when needed.

$scope.buttons = [

{ key: "FIRST" },

{ key: "PREV" },

{ key: "PREV_SEPARATOR", text: "...", type: "separator" },

{ key: "NEXT_SEPARATOR", text: "...", type: "separator" },

{ key: "NEXT" },

{ key: "LAST" }

];

<div class="pagination-panel">

<button class="pagination-button" ng-class="{ 'button-selected': btn.selected == true, 'button-space': btn.type == 'separator' }" ng-repeat="btn in buttons" ng-show="isButtonVisible(btn)" ng-click="onButtonClicked(btn)">

<span ng-class="{ 'button-first': btn.key == 'FIRST', 'button-prev': btn.key == 'PREV', 'button-next': btn.key == 'NEXT', 'button-last': btn.key == 'LAST' }">{{btn.text}}</span>

</button>

</div>

.pagination-panel

{

margin: 0;

padding: 0;

white-space: nowrap;

}

.pagination-button

{

background: #dedede;

border: thin solid #bebebe;

border-radius: 3px;

display: inline-block;

padding: 5px;

margin: 0 2px;

width: 30px;

vertical-align: middle;

width: 32px;

height: 32px;

}

.pagination-button:hover

{

background-color: #cecece;

}

.pagination-button:focus

{

outline: none;

}

.pagination-button > span

{

background-image: url(../../../resources/icons.png);

background-repeat: no-repeat;

display: inline-block;

height: 16px;

margin: 2px 0 0 0;

overflow: hidden;

padding: 0;

width: 16px;

}

.button-selected

{

background: #ababab;

border-color: #999999;

}

.button-space

{

background: transparent;

border: 0;

padding: 0;

width: auto;

vertical-align: bottom;

}

.button-space:hover

{

background: transparent;

border: 0;

}

.button-first

{

background-position: -96px -96px;

}

.button-prev

{

background-position: -112px -96px;

}

.button-next

{

background-position: -128px -96px;

}

.button-last

{

background-position: -144px -96px;

}

Initially, the Grid will show only one data page. Therefore, we need only to have the first page and next page buttons visible. This is all determined from the isButtonVisible function.

var currentPage = 0;

var lastPage = 0;

 

$scope.isButtonVisible = function(obj){

switch (obj.key){

case 'FIRST':

return currentPage >= 4 ? true : false;

case 'PREV':

return currentPage >= 4 ? true : false;

case 'PREV_SEPARATOR':

return currentPage >= 4 ? true : false;

case 'NEXT_SEPARATOR':

return currentPage < lastPage ? true : false;

case 'NEXT':

return (currentPage > 0 && currentPage < 10) ? true : false;

case 'LAST':

return (currentPage < lastPage && currentPage < 10 ) ? true : false;

default:

var buttonIndex = parseInt(obj.key, 10);

return (buttonIndex >= currentPage - 2 && buttonIndex <= currentPage + 2) || (currentPage == lastPage && currentPage - buttonIndex <= 4) ? true : false;

}

 

return true;

}

Load Data from a Remote Data Source

Whenever a button is clicked, depending on its key, a different action is executed. In case of the next page button, we will make a request to load a new data page from the server, if the page is not already loaded.

var loadNewPage = function(){

$gridService.beginLoad($scope.gridName, { type: 'circular', opacity: 0.5 });

var loadTimer = $timeout(function(){

var pageNum = currentPage + 1;

var dataSource = $http.get('data-page-' + pageNum + '.txt');

if (dataSource){

dataSource.success(function(data){

// Suspend the Layout

$gridService.suspendLayout($scope.gridName);

 

// Add Page

for (var i = 0; i < $scope.numRowsPerPage && i < data.length; i++)

$scope.rows.push(data[i]);

 

// Update the Layout

$gridService.resumeLayout($scope.gridName);

 

currentPage++;

$scope.buttons.splice(3 + currentPage - 1, 0, { key: currentPage.toString(), text: currentPage.toString() });

 

updateButtonSelection();

 

$gridService.nextPage($scope.gridName);

lastPage = currentPage;

});

dataSource.error(function(data){

alert("AJAX failed to Load Data");

});

}

 

$timeout.cancel(loadTimer);

}, 1000);

}

 

$scope.onButtonClicked = function(obj){

switch (obj.key){

case 'NEXT':

if (currentPage >= lastPage)

loadNewPage();

else {

currentPage++;

$gridService.nextPage($scope.gridName);

}

break;

}

}

Note In our case, for data source we are using multiple JSON files and the AngularJS $http service to load the data files into the Grid directive. You can easily change that in your application, with real data from a remote data server.

Related: Loading Animations in AngularJS Grid

As code above shows, whenever a new data page is requested, a loading animation is shown to notify the user that data is loading. After data is fully loaded, a new button is created and added to the pagination control panel. This button is also marked as selected so that it notifies to the user the current page number of the data present into the Grid.

Once data page is loaded, we can access it by clicking on corresponding button. The data is already present into the Grid, and we don't have to reload it every time a button is clicked.

Other buttons, like first, previous and last, are showing the first page, previous page and last page respectively. For this purpose, we are using built-in methods from the Grid directive that can change the currently displayed page on demand.

var updateButtonSelection = function(){

for (var i = 0; i < $scope.buttons.length; i++)

$scope.buttons[i].selected = false;

 

$scope.buttons[currentPage+2].selected = true;

}

 

var loadPage = function(index){

currentPage = index;

$gridService.currentPage($scope.gridName, index);

}

 

$scope.onButtonClicked = function(obj){

switch (obj.key){

case 'FIRST':

currentPage = 1;

$gridService.firstPage($scope.gridName);

break;

 

case 'PREV':

currentPage--;

$gridService.prevPage($scope.gridName);

break;

 

case 'NEXT':

if (currentPage >= lastPage)

loadNewPage();

else {

currentPage++;

$gridService.nextPage($scope.gridName);

}

break;

 

case 'LAST':

currentPage = lastPage;

$gridService.lastPage($scope.gridName);

break;

 

default:

loadPage(parseInt(obj.key, 10));

break;

}

 

updateButtonSelection();

}

We are using the following built-in methods to navigate among data pages in the Grid:

  • currentPage - specifies the number of page currently visible in grid view
  • firstPage - the first page is shown in the grid view
  • lastPage - the last page is shown in the grid view
  • nextPage - moves to the next page
  • prevPage - moves to the previous page

The pagination control panel used in this example is a custom made. You can create your own pagination panel, with any other custom HTML elements, that better suite your application requirements. Then by using few built-in built methods, you can easily navigate among data pages in the Grid directive.

Conclusion

To create a server side pagination is simple, you only need to determine the size of the page by specifying the number of rows that you want to see in current view of the Grid directive for AngularJS. Afterwards, you can load data pages on demand from a remote data server.

The control panel used to navigate among pages is optional and fully customizable. There are built-in methods that allow you to create custom pagination controls that better matches the UI of your application.

Did you Like this Article?


Enter your e-mail address below and you will receive latest articles as well as news on upcoming events and special offers.