angular-data-grid-new
Version:
Light, flexible and performant Data Grid for AngularJS apps, with built-in sorting, pagination and filtering options, unified API for client-side and server-side data fetching, seamless synchronization with browser address bar and total freedom in mark-
245 lines (212 loc) • 11.9 kB
JavaScript
(function () {
'use strict';
angular
.module('paging', [])
.factory('paging', ['$parse', function ($parse) {
return {
create: function (ctrl, $scope, $attrs) {
ctrl.setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop;
ctrl.ngModelCtrl = {$setViewValue: angular.noop}; // nullModelCtrl
ctrl.init = function (ngModelCtrl, config) {
ctrl.ngModelCtrl = ngModelCtrl;
ctrl.config = config;
ngModelCtrl.$render = function () {
ctrl.render();
};
if ($attrs.itemsPerPage) {
$scope.$parent.$watch($parse($attrs.itemsPerPage), function (value) {
ctrl.itemsPerPage = parseInt(value, 10);
$scope.totalPages = ctrl.calculateTotalPages();
ctrl.updatePage();
});
} else {
ctrl.itemsPerPage = config.itemsPerPage;
}
$scope.$watch('totalItems', function (newTotal, oldTotal) {
if (angular.isDefined(newTotal) || newTotal !== oldTotal) {
$scope.totalPages = ctrl.calculateTotalPages();
ctrl.updatePage();
}
});
};
ctrl.calculateTotalPages = function () {
var totalPages = ctrl.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / ctrl.itemsPerPage);
return Math.max(totalPages || 0, 1);
};
ctrl.render = function () {
$scope.page = parseInt(ctrl.ngModelCtrl.$viewValue, 10) || 1;
};
$scope.selectPage = function (page, evt) {
if (evt) {
evt.preventDefault();
}
var clickAllowed = !$scope.ngDisabled || !evt;
if (clickAllowed && $scope.page !== page && page > 0 && page <= $scope.totalPages) {
if (evt && evt.target) {
evt.target.blur();
}
ctrl.ngModelCtrl.$setViewValue(page);
ctrl.ngModelCtrl.$render();
}
};
$scope.getText = function (key) {
return $scope[key + 'Text'] || ctrl.config[key + 'Text'];
};
$scope.noPrevious = function () {
return $scope.page === 1;
};
$scope.noNext = function () {
return $scope.page === $scope.totalPages;
};
ctrl.updatePage = function () {
ctrl.setNumPages($scope.$parent, $scope.totalPages); // Readonly variable
if ($scope.page > $scope.totalPages) {
$scope.selectPage($scope.totalPages);
} else {
ctrl.ngModelCtrl.$render();
}
};
}
};
}]);
angular
.module('pagination', ['paging'])
.controller('PaginationController', ['$scope', '$attrs', '$parse', 'paging', 'paginationConfig', function ($scope, $attrs, $parse, paging, paginationConfig) {
var ctrl = this;
// Setup configuration parameters
var maxSize = angular.isDefined($attrs.maxSize) ? $scope.$parent.$eval($attrs.maxSize) : paginationConfig.maxSize,
rotate = angular.isDefined($attrs.rotate) ? $scope.$parent.$eval($attrs.rotate) : paginationConfig.rotate,
forceEllipses = angular.isDefined($attrs.forceEllipses) ? $scope.$parent.$eval($attrs.forceEllipses) : paginationConfig.forceEllipses,
boundaryLinkNumbers = angular.isDefined($attrs.boundaryLinkNumbers) ? $scope.$parent.$eval($attrs.boundaryLinkNumbers) : paginationConfig.boundaryLinkNumbers;
$scope.boundaryLinks = angular.isDefined($attrs.boundaryLinks) ? $scope.$parent.$eval($attrs.boundaryLinks) : paginationConfig.boundaryLinks;
$scope.directionLinks = angular.isDefined($attrs.directionLinks) ? $scope.$parent.$eval($attrs.directionLinks) : paginationConfig.directionLinks;
paging.create(this, $scope, $attrs);
if ($attrs.maxSize) {
$scope.$parent.$watch($parse($attrs.maxSize), function (value) {
maxSize = parseInt(value, 10);
ctrl.render();
});
}
// Create page object used in template
function makePage(number, text, isActive) {
return {
number: number,
text: text,
active: isActive
};
}
function getPages(currentPage, totalPages) {
var pages = [];
// Default page limits
var startPage = 1, endPage = totalPages;
var isMaxSized = angular.isDefined(maxSize) && maxSize < totalPages;
// recompute if maxSize
if (isMaxSized) {
if (rotate) {
// Current page is displayed in the middle of the visible ones
startPage = Math.max(currentPage - Math.floor(maxSize / 2), 1);
endPage = startPage + maxSize - 1;
// Adjust if limit is exceeded
if (endPage > totalPages) {
endPage = totalPages;
startPage = endPage - maxSize + 1;
}
} else {
// Visible pages are paginated with maxSize
startPage = (Math.ceil(currentPage / maxSize) - 1) * maxSize + 1;
// Adjust last page if limit is exceeded
endPage = Math.min(startPage + maxSize - 1, totalPages);
}
}
// Add page number links
for (var number = startPage; number <= endPage; number++) {
var page = makePage(number, number, number === currentPage);
pages.push(page);
}
// Add links to move between page sets
if (isMaxSized && maxSize > 0 && (!rotate || forceEllipses || boundaryLinkNumbers)) {
if (startPage > 1) {
if (!boundaryLinkNumbers || startPage > 3) { //need ellipsis for all options unless range is too close to beginning
var previousPageSet = makePage(startPage - 1, '...', false);
pages.unshift(previousPageSet);
}
if (boundaryLinkNumbers) {
if (startPage === 3) { //need to replace ellipsis when the buttons would be sequential
var secondPageLink = makePage(2, '2', false);
pages.unshift(secondPageLink);
}
//add the first page
var firstPageLink = makePage(1, '1', false);
pages.unshift(firstPageLink);
}
}
if (endPage < totalPages) {
if (!boundaryLinkNumbers || endPage < totalPages - 2) { //need ellipsis for all options unless range is too close to end
var nextPageSet = makePage(endPage + 1, '...', false);
pages.push(nextPageSet);
}
if (boundaryLinkNumbers) {
if (endPage === totalPages - 2) { //need to replace ellipsis when the buttons would be sequential
var secondToLastPageLink = makePage(totalPages - 1, totalPages - 1, false);
pages.push(secondToLastPageLink);
}
//add the last page
var lastPageLink = makePage(totalPages, totalPages, false);
pages.push(lastPageLink);
}
}
}
return pages;
}
var originalRender = this.render;
this.render = function () {
originalRender();
if ($scope.page > 0 && $scope.page <= $scope.totalPages) {
$scope.pages = getPages($scope.page, $scope.totalPages);
}
};
}])
.constant('paginationConfig', {
itemsPerPage: 10,
boundaryLinks: false,
boundaryLinkNumbers: false,
directionLinks: true,
firstText: 'First',
previousText: 'Previous',
nextText: 'Next',
lastText: 'Last',
rotate: true,
forceEllipses: false
})
.directive('gridPagination', ['$parse', 'paginationConfig', function ($parse, paginationConfig) {
return {
scope: {
totalItems: '=',
firstText: '@',
previousText: '@',
nextText: '@',
lastText: '@',
ngDisabled: '='
},
require: ['gridPagination', '?ngModel'],
controller: 'PaginationController',
controllerAs: 'pagination',
templateUrl: function (element, attrs) {
return attrs.templateUrl || 'src/template/pagination/pagination.html';
},
replace: true,
link: function (scope, element, attrs, ctrls) {
var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
if (!ngModelCtrl) {
return; // do nothing if no ng-model
}
paginationCtrl.init(ngModelCtrl, paginationConfig);
}
};
}])
.run(['$templateCache', function ($templateCache) {
$templateCache.put('src/template/pagination/pagination.html',
"<ul class='pagination'><li ng-if='::boundaryLinks' ng-class='{disabled: noPrevious()||ngDisabled}' class='pagination-first'><a href ng-click='selectPage(1, $event)'>{{::getText('first')}}</a></li> <li ng-if='::directionLinks' ng-class='{disabled: noPrevious()||ngDisabled}' class='pagination-prev'><a href ng-click='selectPage(page - 1, $event)'>{{::getText('previous')}}</a></li> <li ng-repeat='page in pages track by $index' ng-class='{active: page.active,disabled: ngDisabled&&!page.active}' class='pagination-page'><a href ng-click='selectPage(page.number, $event)'>{{page.text}}</a></li> <li ng-if='::directionLinks' ng-class='{disabled: noNext()||ngDisabled}' class='pagination-next'><a href ng-click='selectPage(page + 1, $event)'>{{::getText('next')}}</a></li> <li ng-if='::boundaryLinks' ng-class='{disabled: noNext()||ngDisabled}' class='pagination-last'><a href ng-click='selectPage(totalPages, $event)'>{{::getText('last')}}</a></li> </ul>"
);
}]);
})();