Skip to content

Commit

Permalink
feat(pagination): total-items & optional items-per-page API
Browse files Browse the repository at this point in the history
Closes angular-ui#820.

BREAKING CHANGE: API has undergone some changes in order to be easier to use.
 * `current-page` is replaced from `page`.
 * Number of pages is not defined by `num-pages`, but from `total-items` &
  `items-per-page` instead. If `items-per-page` is missing, default is 10.
 * `num-pages` still exists but is just readonly.

  Before:

  <pagination num-pages="10" ...></pagination>

  After:

  <pagination total-items="100" ...></pagination>
  • Loading branch information
bekos committed Aug 23, 2013
1 parent ed5f979 commit 6f9c6ef
Show file tree
Hide file tree
Showing 6 changed files with 670 additions and 688 deletions.
18 changes: 10 additions & 8 deletions src/pagination/docs/demo.html
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
<div ng-controller="PaginationDemoCtrl" class="well well-small">
<h4>Default</h4>

<pagination num-pages="noOfPages" current-page="currentPage"></pagination>
<pagination boundary-links="true" num-pages="noOfPages" current-page="currentPage" class="pagination-small" previous-text="&lsaquo;" next-text="&rsaquo;" first-text="&laquo;" last-text="&raquo;"></pagination>
<pagination direction-links="false" boundary-links="true" num-pages="noOfPages" current-page="currentPage"></pagination>
<pagination direction-links="false" num-pages="noOfPages" current-page="currentPage"></pagination>
<pagination total-items="totalItems" page="currentPage"></pagination>
<pagination boundary-links="true" total-items="totalItems" page="currentPage" class="pagination-small" previous-text="&lsaquo;" next-text="&rsaquo;" first-text="&laquo;" last-text="&raquo;"></pagination>
<pagination direction-links="false" boundary-links="true" total-items="totalItems" page="currentPage"></pagination>
<pagination direction-links="false" total-items="totalItems" page="currentPage" num-pages="smallnumPages"></pagination>

<button class="btn" ng-click="setPage(3)">Set current page to: 3</button>
The selected page no: {{currentPage}}

<hr />
<h4>Pager</h4>
<pager num-pages="noOfPages" current-page="currentPage"></pager>
<pager total-items="totalItems" page="currentPage"></pager>

<hr />
<h4>Limit the maximimum visible page-buttons</h4>
<pagination num-pages="bigNoOfPages" current-page="bigCurrentPage" max-size="maxSize" class="pagination-small" boundary-links="true"></pagination>
<pagination rotate="false" num-pages="bigNoOfPages" current-page="bigCurrentPage" max-size="maxSize" class="pagination-small" boundary-links="true"></pagination>
<h4>Limit the maximimum visible buttons</h4>
<pagination total-items="bigTotalItems" page="bigCurrentPage" max-size="maxSize" class="pagination-small" boundary-links="true"></pagination>
<pagination total-items="bigTotalItems" page="bigCurrentPage" max-size="maxSize" class="pagination-small" boundary-links="true" rotate="false" num-pages="numPages"></pagination>

<pre>Page: {{bigCurrentPage}} / {{numPages}}</pre>
</div>
4 changes: 2 additions & 2 deletions src/pagination/docs/demo.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
var PaginationDemoCtrl = function ($scope) {
$scope.noOfPages = 7;
$scope.totalItems = 64;
$scope.currentPage = 4;
$scope.maxSize = 5;

$scope.setPage = function (pageNo) {
$scope.currentPage = pageNo;
};

$scope.bigNoOfPages = 18;
$scope.bigTotalItems = 175;
$scope.bigCurrentPage = 1;
};
18 changes: 13 additions & 5 deletions src/pagination/docs/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,26 @@ A lightweight pagination directive that is focused on ... providing pagination &

Settings can be provided as attributes in the `<pagination>` or globally configured through the `paginationConfig`.

* `num-pages` <i class="icon-eye-open"></i>
* `page` <i class="icon-eye-open"></i>
:
Number of total pages.
Current page number. First page is 1.

* `current-page` <i class="icon-eye-open"></i>
* `total-items` <i class="icon-eye-open"></i>
:
Current page number.
Total number of items in all pages.

* `items-per-page` <i class="icon-eye-open"></i>
_(Defaults: 10)_ :
Maximum number of items per page. A value less than one indicates all items on one page.

* `max-size` <i class="icon-eye-open"></i>
_(Defaults: null)_ :
Limit number for pagination size.

* `num-pages` <small class="badge">readonly</small>
:
Total number of pages to display.

* `rotate`
_(Defaults: true)_ :
Whether to keep current page in the middle of the visible ones.
Expand Down Expand Up @@ -52,7 +60,7 @@ Settings can be provided as attributes in the `<pagination>` or globally configu
### Pager Settings ###

Settings can be provided as attributes in the `<pager>` or globally configured through the `pagerConfig`.
For `num-pages`, `current-page` and `on-select-page (page)` see pagination settings. Other settings are:
For `page`, `total-items`, `items-per-page`, `num-pages` and `on-select-page (page)` see pagination settings. Other settings are:

* `align`
_(Default: true)_ :
Expand Down
172 changes: 107 additions & 65 deletions src/pagination/pagination.js
Original file line number Diff line number Diff line change
@@ -1,43 +1,73 @@
angular.module('ui.bootstrap.pagination', [])

.controller('PaginationController', ['$scope', '$interpolate', function ($scope, $interpolate) {
.controller('PaginationController', ['$scope', '$attrs', '$parse', '$interpolate', function ($scope, $attrs, $parse, $interpolate) {
var self = this;

this.currentPage = 1;
this.init = function(defaultItemsPerPage) {
if ($attrs.itemsPerPage) {
$scope.$parent.$watch($parse($attrs.itemsPerPage), function(value) {
self.itemsPerPage = parseInt(value, 10);
$scope.totalPages = self.calculateTotalPages();
});
} else {
this.itemsPerPage = defaultItemsPerPage;
}
};

this.noPrevious = function() {
return this.currentPage === 1;
return this.page === 1;
};
this.noNext = function() {
return this.currentPage === $scope.numPages;
return this.page === $scope.totalPages;
};

this.isActive = function(page) {
return this.currentPage === page;
return this.page === page;
};

this.reset = function() {
$scope.pages = [];
this.currentPage = parseInt($scope.currentPage, 10);
this.calculateTotalPages = function() {
return this.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / this.itemsPerPage);
};

if ( this.currentPage > $scope.numPages ) {
$scope.selectPage($scope.numPages);
}
this.getAttributeValue = function(attribute, defaultValue, interpolate) {
return angular.isDefined(attribute) ? (interpolate ? $interpolate(attribute)($scope.$parent) : $scope.$parent.$eval(attribute)) : defaultValue;
};

this.render = function() {
this.page = parseInt($scope.page, 10) || 1;
$scope.pages = this.getPages(this.page, $scope.totalPages);
};

var self = this;
$scope.selectPage = function(page) {
if ( ! self.isActive(page) && page > 0 && page <= $scope.numPages) {
$scope.currentPage = page;
if ( ! self.isActive(page) && page > 0 && page <= $scope.totalPages) {
$scope.page = page;
$scope.onSelectPage({ page: page });
}
};

this.getAttributeValue = function(attribute, defaultValue, interpolate) {
return angular.isDefined(attribute) ? (interpolate ? $interpolate(attribute)($scope.$parent) : $scope.$parent.$eval(attribute)) : defaultValue;
};
$scope.$watch('totalItems', function() {
$scope.totalPages = self.calculateTotalPages();
});

$scope.$watch('totalPages', function(value) {
if ( $attrs.numPages ) {
$scope.numPages = value; // Readonly variable
}

if ( self.page > value ) {
$scope.selectPage(value);
} else {
self.render();
}
});

$scope.$watch('page', function() {
self.render();
});
}])

.constant('paginationConfig', {
itemsPerPage: 10,
boundaryLinks: false,
directionLinks: true,
firstText: 'First',
Expand All @@ -47,28 +77,38 @@ angular.module('ui.bootstrap.pagination', [])
rotate: true
})

.directive('pagination', ['paginationConfig', function(config) {
.directive('pagination', ['$parse', 'paginationConfig', function($parse, config) {
return {
restrict: 'EA',
scope: {
numPages: '=',
currentPage: '=',
maxSize: '=',
onSelectPage: '&'
page: '=',
totalItems: '=',
onSelectPage:' &',
numPages: '='
},
controller: 'PaginationController',
templateUrl: 'template/pagination/pagination.html',
replace: true,
link: function(scope, element, attrs, paginationCtrl) {

// Setup configuration parameters
var boundaryLinks = paginationCtrl.getAttributeValue(attrs.boundaryLinks, config.boundaryLinks ),
directionLinks = paginationCtrl.getAttributeValue(attrs.directionLinks, config.directionLinks ),
firstText = paginationCtrl.getAttributeValue(attrs.firstText, config.firstText, true),
previousText = paginationCtrl.getAttributeValue(attrs.previousText, config.previousText, true),
nextText = paginationCtrl.getAttributeValue(attrs.nextText, config.nextText, true),
lastText = paginationCtrl.getAttributeValue(attrs.lastText, config.lastText, true),
rotate = paginationCtrl.getAttributeValue(attrs.rotate, config.rotate);
var maxSize,
boundaryLinks = paginationCtrl.getAttributeValue(attrs.boundaryLinks, config.boundaryLinks ),
directionLinks = paginationCtrl.getAttributeValue(attrs.directionLinks, config.directionLinks ),
firstText = paginationCtrl.getAttributeValue(attrs.firstText, config.firstText, true),
previousText = paginationCtrl.getAttributeValue(attrs.previousText, config.previousText, true),
nextText = paginationCtrl.getAttributeValue(attrs.nextText, config.nextText, true),
lastText = paginationCtrl.getAttributeValue(attrs.lastText, config.lastText, true),
rotate = paginationCtrl.getAttributeValue(attrs.rotate, config.rotate);

paginationCtrl.init(config.itemsPerPage);

if (attrs.maxSize) {
scope.$parent.$watch($parse(attrs.maxSize), function(value) {
maxSize = parseInt(value, 10);
paginationCtrl.render();
});
}

// Create page object used in template
function makePage(number, text, isActive, isDisabled) {
Expand All @@ -80,76 +120,79 @@ angular.module('ui.bootstrap.pagination', [])
};
}

scope.$watch('numPages + currentPage + maxSize', function() {
paginationCtrl.reset();
paginationCtrl.getPages = function(currentPage, totalPages) {
var pages = [];

// Default page limits
var startPage = 1, endPage = scope.numPages;
var isMaxSized = ( angular.isDefined(scope.maxSize) && scope.maxSize < scope.numPages );
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(paginationCtrl.currentPage - Math.floor(scope.maxSize/2), 1);
endPage = startPage + scope.maxSize - 1;
startPage = Math.max(currentPage - Math.floor(maxSize/2), 1);
endPage = startPage + maxSize - 1;

// Adjust if limit is exceeded
if (endPage > scope.numPages) {
endPage = scope.numPages;
startPage = endPage - scope.maxSize + 1;
if (endPage > totalPages) {
endPage = totalPages;
startPage = endPage - maxSize + 1;
}
} else {
// Visible pages are paginated with maxSize
startPage = ((Math.ceil(paginationCtrl.currentPage / scope.maxSize) - 1) * scope.maxSize) + 1;
startPage = ((Math.ceil(currentPage / maxSize) - 1) * maxSize) + 1;

// Adjust last page if limit is exceeded
endPage = Math.min(startPage + scope.maxSize - 1, scope.numPages);
endPage = Math.min(startPage + maxSize - 1, totalPages);
}
}

// Add page number links
for (var number = startPage; number <= endPage; number++) {
var page = makePage(number, number, paginationCtrl.isActive(number), false);
scope.pages.push(page);
pages.push(page);
}

// Add links to move between page sets
if ( isMaxSized && ! rotate ) {
if ( startPage > 1 ) {
var previousPageSet = makePage(startPage - 1, '...', false, false);
scope.pages.unshift(previousPageSet);
pages.unshift(previousPageSet);
}

if ( endPage < scope.numPages ) {
if ( endPage < totalPages ) {
var nextPageSet = makePage(endPage + 1, '...', false, false);
scope.pages.push(nextPageSet);
pages.push(nextPageSet);
}
}

// Add previous & next links
if (directionLinks) {
var previousPage = makePage(paginationCtrl.currentPage - 1, previousText, false, paginationCtrl.noPrevious());
scope.pages.unshift(previousPage);
var previousPage = makePage(currentPage - 1, previousText, false, paginationCtrl.noPrevious());
pages.unshift(previousPage);

var nextPage = makePage(paginationCtrl.currentPage + 1, nextText, false, paginationCtrl.noNext());
scope.pages.push(nextPage);
var nextPage = makePage(currentPage + 1, nextText, false, paginationCtrl.noNext());
pages.push(nextPage);
}

// Add first & last links
if (boundaryLinks) {
var firstPage = makePage(1, firstText, false, paginationCtrl.noPrevious());
scope.pages.unshift(firstPage);
pages.unshift(firstPage);

var lastPage = makePage(scope.numPages, lastText, false, paginationCtrl.noNext());
scope.pages.push(lastPage);
var lastPage = makePage(totalPages, lastText, false, paginationCtrl.noNext());
pages.push(lastPage);
}
});

return pages;
};
}
};
}])

.constant('pagerConfig', {
itemsPerPage: 10,
previousText: '« Previous',
nextText: 'Next »',
align: true
Expand All @@ -159,9 +202,10 @@ angular.module('ui.bootstrap.pagination', [])
return {
restrict: 'EA',
scope: {
numPages: '=',
currentPage: '=',
onSelectPage: '&'
page: '=',
totalItems: '=',
onSelectPage:' &',
numPages: '='
},
controller: 'PaginationController',
templateUrl: 'template/pagination/pager.html',
Expand All @@ -173,6 +217,8 @@ angular.module('ui.bootstrap.pagination', [])
nextText = paginationCtrl.getAttributeValue(attrs.nextText, config.nextText, true),
align = paginationCtrl.getAttributeValue(attrs.align, config.align);

paginationCtrl.init(config.itemsPerPage);

// Create page object used in template
function makePage(number, text, isDisabled, isPrevious, isNext) {
return {
Expand All @@ -184,16 +230,12 @@ angular.module('ui.bootstrap.pagination', [])
};
}

scope.$watch('numPages + currentPage', function() {
paginationCtrl.reset();

// Add previous & next links
var previousPage = makePage(paginationCtrl.currentPage - 1, previousText, paginationCtrl.noPrevious(), true, false);
scope.pages.unshift(previousPage);

var nextPage = makePage(paginationCtrl.currentPage + 1, nextText, paginationCtrl.noNext(), false, true);
scope.pages.push(nextPage);
});
paginationCtrl.getPages = function(currentPage) {
return [
makePage(currentPage - 1, previousText, paginationCtrl.noPrevious(), true, false),
makePage(currentPage + 1, nextText, paginationCtrl.noNext(), false, true)
];
};
}
};
}]);
Loading

0 comments on commit 6f9c6ef

Please sign in to comment.