rotate defaulted to true and force-ellipses set to true:
+
+
rotate set to false:
+
+
boundary-link-numbers set to true and rotate defaulted to true:
+
+
boundary-link-numbers set to true and rotate set to false:
+
+
Page: {{bigCurrentPage}} / {{numPages}}
+
Pager
-
-
Limit the maximum visible buttons
-
-
-
Page: {{bigCurrentPage}} / {{numPages}}
diff --git a/src/pagination/docs/readme.md b/src/pagination/docs/readme.md
index 07c89d5ed3..74f483179c 100644
--- a/src/pagination/docs/readme.md
+++ b/src/pagination/docs/readme.md
@@ -36,6 +36,10 @@ Settings can be provided as attributes in the `` or globally con
* `rotate`
_(Defaults: true)_ :
Whether to keep current page in the middle of the visible ones.
+
+ * `force-ellipses`
+ _(Defaults: false)_ :
+ Also displays ellipses when `rotate` is true and `max-size` is smaller than the number of pages.
* `direction-links`
_(Default: true)_ :
@@ -60,6 +64,10 @@ Settings can be provided as attributes in the `` or globally con
* `last-text`
_(Default: 'Last')_ :
Text for Last button.
+
+ * `boundary-link-numbers`
+ _(Default: false)_ :
+ Whether to always display the first and last page numbers. If `max-size` is smaller than the number of pages, then the first and last page numbers are still shown with ellipses in-between as necessary. NOTE: `max-size` refers to the center of the range. This option may add up to 2 more numbers on each side of the displayed range for the end value and what would be an ellipsis but is replaced by a number because it is sequential.
* `template-url`
_(Default: 'template/pagination/pagination.html')_ :
diff --git a/src/pagination/pagination.js b/src/pagination/pagination.js
index 90606eef73..6ae49a2a04 100644
--- a/src/pagination/pagination.js
+++ b/src/pagination/pagination.js
@@ -24,7 +24,7 @@ angular.module('ui.bootstrap.pagination', [])
$scope.$watch('totalItems', function(newTotal, oldTotal) {
if (angular.isDefined(newTotal) || newTotal !== oldTotal) {
- $scope.totalPages = self.calculateTotalPages();
+ $scope.totalPages = self.calculateTotalPages();
updatePage();
}
});
@@ -80,12 +80,14 @@ angular.module('ui.bootstrap.pagination', [])
.constant('uibPaginationConfig', {
itemsPerPage: 10,
boundaryLinks: false,
+ boundaryLinkNumbers: false,
directionLinks: true,
firstText: 'First',
previousText: 'Previous',
nextText: 'Next',
lastText: 'Last',
- rotate: true
+ rotate: true,
+ forceEllipses: false
})
.directive('uibPagination', ['$parse', 'uibPaginationConfig', function($parse, paginationConfig) {
@@ -115,7 +117,9 @@ angular.module('ui.bootstrap.pagination', [])
// 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;
+ 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;
@@ -148,7 +152,7 @@ angular.module('ui.bootstrap.pagination', [])
if (isMaxSized) {
if (rotate) {
// Current page is displayed in the middle of the visible ones
- startPage = Math.max(currentPage - Math.floor(maxSize/2), 1);
+ startPage = Math.max(currentPage - Math.floor(maxSize / 2), 1);
endPage = startPage + maxSize - 1;
// Adjust if limit is exceeded
@@ -158,7 +162,7 @@ angular.module('ui.bootstrap.pagination', [])
}
} else {
// Visible pages are paginated with maxSize
- startPage = ((Math.ceil(currentPage / maxSize) - 1) * maxSize) + 1;
+ startPage = (Math.ceil(currentPage / maxSize) - 1) * maxSize + 1;
// Adjust last page if limit is exceeded
endPage = Math.min(startPage + maxSize - 1, totalPages);
@@ -172,16 +176,38 @@ angular.module('ui.bootstrap.pagination', [])
}
// Add links to move between page sets
- if ( isMaxSized && maxSize > 0 ) {
- if ( startPage > 1 ) {
+ 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;
}
diff --git a/src/pagination/test/pagination.spec.js b/src/pagination/test/pagination.spec.js
index 292807f8ec..d94184d2ba 100644
--- a/src/pagination/test/pagination.spec.js
+++ b/src/pagination/test/pagination.spec.js
@@ -24,9 +24,9 @@ describe('pagination directive', function() {
}
// Returns a comma-separated string that represents the pager, like: "Prev, 1, 2, 3, Next"
- function getPaginationAsText(){
+ function getPaginationAsText() {
var len = getPaginationBarSize(), outItems = [];
- for(var i = 0; i < len; i++){
+ for (var i = 0; i < len; i++) {
outItems.push(getPaginationEl(i).text());
}
return outItems.join(', ');
@@ -290,8 +290,8 @@ describe('pagination directive', function() {
$rootScope.$digest();
});
- it('contains maxsize + 3 li elements', function() {
- expect(getPaginationBarSize()).toBe($rootScope.maxSize + 3);
+ it('contains maxsize + 2 li elements', function() {
+ expect(getPaginationBarSize()).toBe($rootScope.maxSize + 2);
expect(getPaginationEl(0).text()).toBe('Previous');
expect(getPaginationEl(-1).text()).toBe('Next');
});
@@ -309,8 +309,8 @@ describe('pagination directive', function() {
clickPaginationEl(-1);
expect($rootScope.currentPage).toBe(7);
- expect(getPaginationEl(4)).toHaveClass('active');
- expect(getPaginationEl(4).text()).toBe(''+$rootScope.currentPage);
+ expect(getPaginationEl(3)).toHaveClass('active');
+ expect(getPaginationEl(3).text()).toBe(''+$rootScope.currentPage);
});
it('shows the page number in middle after the prev link is clicked', function() {
@@ -318,14 +318,14 @@ describe('pagination directive', function() {
clickPaginationEl(0);
expect($rootScope.currentPage).toBe(6);
- expect(getPaginationEl(4)).toHaveClass('active');
- expect(getPaginationEl(4).text()).toBe(''+$rootScope.currentPage);
+ expect(getPaginationEl(3)).toHaveClass('active');
+ expect(getPaginationEl(3).text()).toBe(''+$rootScope.currentPage);
});
it('changes pagination bar size when max-size value changed', function() {
$rootScope.maxSize = 7;
$rootScope.$digest();
- expect(getPaginationBarSize()).toBe(10);
+ expect(getPaginationBarSize()).toBe(9);
});
it('sets the pagination bar size to num-pages, if max-size is greater than num-pages ', function() {
@@ -360,6 +360,47 @@ describe('pagination directive', function() {
element.remove();
});
+ });
+
+ describe('with `force-ellipses` option', function() {
+ beforeEach(function() {
+ $rootScope.total = 98; // 10 pages
+ $rootScope.currentPage = 3;
+ $rootScope.maxSize = 5;
+ element = $compile('')($rootScope);
+ $rootScope.$digest();
+ });
+
+ it('contains maxsize + 3 li elements', function() {
+ expect(getPaginationBarSize()).toBe($rootScope.maxSize + 3);
+ expect(getPaginationEl(0).text()).toBe('Previous');
+ expect(getPaginationEl(-1).text()).toBe('Next');
+ expect(getPaginationEl(-2).text()).toBe('...');
+ });
+
+ it('shows the page number in middle after the next link is clicked', function() {
+ updateCurrentPage(6);
+ clickPaginationEl(-1);
+
+ expect($rootScope.currentPage).toBe(7);
+ expect(getPaginationEl(4)).toHaveClass('active');
+ expect(getPaginationEl(4).text()).toBe(''+$rootScope.currentPage);
+ });
+
+ it('shows the page number in middle after the prev link is clicked', function() {
+ updateCurrentPage(7);
+ clickPaginationEl(0);
+
+ expect($rootScope.currentPage).toBe(6);
+ expect(getPaginationEl(4)).toHaveClass('active');
+ expect(getPaginationEl(4).text()).toBe(''+$rootScope.currentPage);
+ });
+
+ it('changes pagination bar size when max-size value changed', function() {
+ $rootScope.maxSize = 7;
+ $rootScope.$digest();
+ expect(getPaginationBarSize()).toBe(10);
+ });
it('should display an ellipsis on the right if the last displayed page\'s number is less than the last page', function() {
updateCurrentPage(1);
@@ -369,20 +410,112 @@ describe('pagination directive', function() {
it('should display an ellipsis on the left if the first displayed page\'s number is greater than 1', function() {
updateCurrentPage(10);
expect(getPaginationAsText()).toBe('Previous, ..., 6, 7, 8, 9, 10, Next');
- });
+ });
it('should display both ellipsis\' if the displayed range is in the middle', function() {
updateCurrentPage(5);
expect(getPaginationAsText()).toBe('Previous, ..., 3, 4, 5, 6, 7, ..., Next');
});
- it('should not display any ellipsis\' if the number of pages >= maxsize', function() {
+ it('should not display any ellipses if the number of pages >= maxsize', function() {
$rootScope.maxSize = 10;
$rootScope.$digest();
expect(getPaginationAsText()).toBe('Previous, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, Next');
});
});
+ describe('with `boundary-link-numbers` option', function() {
+ beforeEach(function() {
+ $rootScope.total = 98; // 10 pages
+ $rootScope.currentPage = 3;
+ $rootScope.maxSize = 5;
+ element = $compile('')($rootScope);
+ $rootScope.$digest();
+ });
+
+ it('contains maxsize + 4 li elements', function() {
+ expect(getPaginationBarSize()).toBe($rootScope.maxSize + 4);
+ expect(getPaginationEl(0).text()).toBe('Previous');
+ expect(getPaginationEl(-1).text()).toBe('Next');
+ expect(getPaginationEl(-2).text()).toBe('10');
+ expect(getPaginationEl(-3).text()).toBe('...');
+ });
+
+ it('shows the page number in middle after the next link is clicked', function() {
+ updateCurrentPage(6);
+ clickPaginationEl(-1);
+
+ expect($rootScope.currentPage).toBe(7);
+ expect(getPaginationEl(5)).toHaveClass('active');
+ expect(getPaginationEl(5).text()).toBe(''+$rootScope.currentPage);
+ });
+
+ it('shows the page number in middle after the prev link is clicked', function() {
+ updateCurrentPage(7);
+ clickPaginationEl(0);
+
+ expect($rootScope.currentPage).toBe(6);
+ expect(getPaginationEl(5)).toHaveClass('active');
+ expect(getPaginationEl(5).text()).toBe(''+$rootScope.currentPage);
+ });
+
+ it('changes pagination bar size when max-size value changed', function() {
+ $rootScope.maxSize = 7;
+ $rootScope.$digest();
+ expect(getPaginationBarSize()).toBe(11);
+ });
+
+ it('should display an ellipsis on the right if the last displayed page\'s number is less than the last page', function() {
+ updateCurrentPage(1);
+ expect(getPaginationAsText()).toBe('Previous, 1, 2, 3, 4, 5, ..., 10, Next');
+ });
+
+ it('should display an ellipsis on the left if the first displayed page\'s number is greater than 1', function() {
+ updateCurrentPage(10);
+ expect(getPaginationAsText()).toBe('Previous, 1, ..., 6, 7, 8, 9, 10, Next');
+ });
+
+ it('should display both ellipses if the displayed range is in the middle', function() {
+ $rootScope.maxSize = 3;
+ $rootScope.$digest();
+ updateCurrentPage(6);
+ expect(getPaginationAsText()).toBe('Previous, 1, ..., 5, 6, 7, ..., 10, Next');
+ });
+
+ it('should not display any ellipses if the number of pages >= maxsize', function() {
+ $rootScope.maxSize = 10;
+ $rootScope.$digest();
+ expect(getPaginationAsText()).toBe('Previous, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, Next');
+ });
+
+ it('should not display an ellipsis on the left if the start page is 2', function() {
+ updateCurrentPage(4);
+ expect(getPaginationAsText()).toBe('Previous, 1, 2, 3, 4, 5, 6, ..., 10, Next');
+ });
+
+ it('should not display an ellipsis on the left if the start page is 3', function() {
+ updateCurrentPage(5);
+ expect(getPaginationAsText()).toBe('Previous, 1, 2, 3, 4, 5, 6, 7, ..., 10, Next');
+ });
+
+ it('should not display an ellipsis on the right if the end page is totalPages - 1', function() {
+ updateCurrentPage(7);
+ expect(getPaginationAsText()).toBe('Previous, 1, ..., 5, 6, 7, 8, 9, 10, Next');
+ });
+
+ it('should not display an ellipsis on the right if the end page is totalPages - 2', function() {
+ updateCurrentPage(6);
+ expect(getPaginationAsText()).toBe('Previous, 1, ..., 4, 5, 6, 7, 8, 9, 10, Next');
+ });
+
+ it('should not display any ellipses if the number of pages <= maxsize + 4 and current page is in center', function() {
+ $rootScope.total = 88; // 9 pages
+ $rootScope.$digest();
+ updateCurrentPage(5);
+ expect(getPaginationAsText()).toBe('Previous, 1, 2, 3, 4, 5, 6, 7, 8, 9, Next');
+ });
+ });
+
describe('with `max-size` option & no `rotate`', function() {
beforeEach(function() {
$rootScope.total = 115; // 12 pages
@@ -549,7 +682,7 @@ describe('pagination directive', function() {
expect(linkEl).not.toHaveFocus();
element.remove();
- });
+ });
it('should blur the "last" link after it has been clicked', function() {
body.append(element);
@@ -648,7 +781,7 @@ describe('pagination directive', function() {
});
});
- describe('`num-pages`', function () {
+ describe('`num-pages`', function() {
beforeEach(function() {
$rootScope.numpg = null;
element = $compile('')($rootScope);
@@ -716,9 +849,29 @@ describe('pagination directive', function() {
element = $compile('')($rootScope);
$rootScope.$digest();
- // Should contain 2 nav buttons, 2 pages, and 2 ellipsis, since the currentPage defaults to 3, which is in the middle
+ expect(getPaginationBarSize()).toBe(4);
+ });
+
+ it('should take forceEllipses defaults into account', function () {
+ paginationConfig.forceEllipses = true;
+ element = $compile('')($rootScope);
+ $rootScope.$digest();
+
+ // Should contain 2 nav buttons, 2 pages, and 2 ellipsis since the currentPage defaults to 3, which is in the middle
expect(getPaginationBarSize()).toBe(6);
});
+
+ it('should take boundaryLinkNumbers defaults into account', function () {
+ paginationConfig.boundaryLinkNumbers = true;
+ $rootScope.total = 88; // 9 pages
+ $rootScope.currentPage = 5;
+ element = $compile('')($rootScope);
+ $rootScope.$digest();
+
+ // Should contain 2 nav buttons, 2 pages, 2 ellipsis, and 2 extra end numbers since the currentPage is in the middle
+ expect(getPaginationBarSize()).toBe(9);
+ expect(getPaginationAsText()).toBe('Previous, 1, ..., 4, 5, 6, ..., 9, Next');
+ });
});
describe('override configuration from attributes', function() {