diff --git a/src/pagination/docs/demo.html b/src/pagination/docs/demo.html
index 4629364907..bb7e608676 100644
--- a/src/pagination/docs/demo.html
+++ b/src/pagination/docs/demo.html
@@ -1,8 +1,9 @@
-
-
+
+
+
The selected page no: {{currentPage}}
\ No newline at end of file
diff --git a/src/pagination/docs/readme.md b/src/pagination/docs/readme.md
index 3008a2b67f..b40617116f 100644
--- a/src/pagination/docs/readme.md
+++ b/src/pagination/docs/readme.md
@@ -1,5 +1,5 @@
A lightweight pagination directive that is focused on ... providing pagination!
-It will take care of visualising a pagination bar. Additionally it will make sure that the state (enabled / disabled) of the Previous / Next buttons is maintained correctly.
+It will take care of visualising a pagination bar. Additionally it will make sure that the state (enabled / disabled) of the Previous / Next and First / Last buttons (if exist) is maintained correctly.
It also provides optional attribute max-size to limit the size of pagination bar.
\ No newline at end of file
diff --git a/src/pagination/pagination.js b/src/pagination/pagination.js
index b66bab5aba..bba5f11ed8 100644
--- a/src/pagination/pagination.js
+++ b/src/pagination/pagination.js
@@ -1,19 +1,45 @@
angular.module('ui.bootstrap.pagination', [])
-.directive('pagination', function() {
+.constant('paginationConfig', {
+ boundaryLinks: false,
+ directionLinks: true,
+ firstText: 'First',
+ previousText: 'Previous',
+ nextText: 'Next',
+ lastText: 'Last'
+})
+
+.directive('pagination', ['paginationConfig', function(paginationConfig) {
return {
restrict: 'EA',
scope: {
numPages: '=',
currentPage: '=',
maxSize: '=',
- onSelectPage: '&',
- nextText: '@',
- previousText: '@'
+ onSelectPage: '&'
},
templateUrl: 'template/pagination/pagination.html',
replace: true,
- link: function(scope) {
+ link: function(scope, element, attrs) {
+
+ // Setup configuration parameters
+ var boundaryLinks = angular.isDefined(attrs.boundaryLinks) ? scope.$eval(attrs.boundaryLinks) : paginationConfig.boundaryLinks;
+ var directionLinks = angular.isDefined(attrs.directionLinks) ? scope.$eval(attrs.directionLinks) : paginationConfig.directionLinks;
+ var firstText = angular.isDefined(attrs.firstText) ? attrs.firstText : paginationConfig.firstText;
+ var previousText = angular.isDefined(attrs.previousText) ? attrs.previousText : paginationConfig.previousText;
+ var nextText = angular.isDefined(attrs.nextText) ? attrs.nextText : paginationConfig.nextText;
+ var lastText = angular.isDefined(attrs.lastText) ? attrs.lastText : paginationConfig.lastText;
+
+ // Create page object used in template
+ function makePage(number, text, isActive, isDisabled) {
+ return {
+ number: number,
+ text: text,
+ active: isActive,
+ disabled: isDisabled
+ };
+ }
+
scope.$watch('numPages + currentPage + maxSize', function() {
scope.pages = [];
@@ -29,9 +55,31 @@ angular.module('ui.bootstrap.pagination', [])
startPage = startPage - ((startPage + maxSize - 1) - scope.numPages );
}
- for(var i=0; i < maxSize && i < scope.numPages ;i++) {
- scope.pages.push(startPage + i);
+ // Add page number links
+ for (var number = startPage, max = startPage + maxSize; number < max; number++) {
+ var page = makePage(number, number, scope.isActive(number), false);
+ scope.pages.push(page);
}
+
+ // Add previous & next links
+ if (directionLinks) {
+ var previousPage = makePage(scope.currentPage - 1, previousText, false, scope.noPrevious());
+ scope.pages.unshift(previousPage);
+
+ var nextPage = makePage(scope.currentPage + 1, nextText, false, scope.noNext());
+ scope.pages.push(nextPage);
+ }
+
+ // Add first & last links
+ if (boundaryLinks) {
+ var firstPage = makePage(1, firstText, false, scope.noPrevious());
+ scope.pages.unshift(firstPage);
+
+ var lastPage = makePage(scope.numPages, lastText, false, scope.noNext());
+ scope.pages.push(lastPage);
+ }
+
+
if ( scope.currentPage > scope.numPages ) {
scope.selectPage(scope.numPages);
}
@@ -47,22 +95,11 @@ angular.module('ui.bootstrap.pagination', [])
};
scope.selectPage = function(page) {
- if ( ! scope.isActive(page) ) {
+ if ( ! scope.isActive(page) && page > 0 && page <= scope.numPages) {
scope.currentPage = page;
scope.onSelectPage({ page: page });
}
};
-
- scope.selectPrevious = function() {
- if ( !scope.noPrevious() ) {
- scope.selectPage(scope.currentPage-1);
- }
- };
- scope.selectNext = function() {
- if ( !scope.noNext() ) {
- scope.selectPage(scope.currentPage+1);
- }
- };
}
};
-});
\ No newline at end of file
+}]);
\ No newline at end of file
diff --git a/src/pagination/test/pagination.spec.js b/src/pagination/test/pagination.spec.js
index 356168432a..1a49709ce4 100644
--- a/src/pagination/test/pagination.spec.js
+++ b/src/pagination/test/pagination.spec.js
@@ -1,4 +1,4 @@
-describe('pagination directive', function () {
+describe('pagination directive with default configuration', function () {
var $rootScope, element;
beforeEach(module('ui.bootstrap.pagination'));
beforeEach(module('template/pagination/pagination.html'));
@@ -192,7 +192,7 @@ describe('pagination directive with max size option', function () {
});
-describe('pagination custom', function () {
+describe('pagination directive with added first & last links', function () {
var $rootScope, element;
beforeEach(module('ui.bootstrap.pagination'));
beforeEach(module('template/pagination/pagination.html'));
@@ -201,12 +201,305 @@ describe('pagination custom', function () {
$rootScope = _$rootScope_;
$rootScope.numPages = 5;
$rootScope.currentPage = 3;
- element = $compile('')($rootScope);
+ element = $compile('')($rootScope);
$rootScope.$digest();
}));
+
+ it('contains one ul and num-pages + 4 li elements', function() {
+ expect(element.find('ul').length).toBe(1);
+ expect(element.find('li').length).toBe(9);
+ expect(element.find('li').eq(0).text()).toBe('First');
+ expect(element.find('li').eq(1).text()).toBe('Previous');
+ expect(element.find('li').eq(-2).text()).toBe('Next');
+ expect(element.find('li').eq(-1).text()).toBe('Last');
+ });
+
+ it('has first and last li visible & with borders', function() {
+ var firstLiEl = element.find('li').eq(0);
+ var lastLiEl = element.find('li').eq(-1);
+
+ expect(firstLiEl.text()).toBe('First');
+ expect(firstLiEl.css('display')).not.toBe('none');
+ expect(lastLiEl.text()).toBe('Last');
+ expect(lastLiEl.css('display')).not.toBe('none');
+ });
+
+
+ it('disables the "first" & "previous" link if current-page is 1', function() {
+ $rootScope.currentPage = 1;
+ $rootScope.$digest();
+ expect(element.find('li').eq(0).hasClass('disabled')).toBe(true);
+ expect(element.find('li').eq(1).hasClass('disabled')).toBe(true);
+ });
+
+ it('disables the "last" & "next" link if current-page is num-pages', function() {
+ $rootScope.currentPage = 5;
+ $rootScope.$digest();
+ expect(element.find('li').eq(-2).hasClass('disabled')).toBe(true);
+ expect(element.find('li').eq(-1).hasClass('disabled')).toBe(true);
+ });
+
+
+ it('changes currentPage if the "first" link is clicked', function() {
+ var first = element.find('li').eq(0).find('a').eq(0);
+ first.click();
+ $rootScope.$digest();
+ expect($rootScope.currentPage).toBe(1);
+ });
+
+ it('changes currentPage if the "last" link is clicked', function() {
+ var last = element.find('li').eq(-1).find('a').eq(0);
+ last.click();
+ $rootScope.$digest();
+ expect($rootScope.currentPage).toBe($rootScope.numPages);
+ });
+
+ it('does not change the current page on "first" click if already at first page', function() {
+ var first = element.find('li').eq(0).find('a').eq(0);
+ $rootScope.currentPage = 1;
+ $rootScope.$digest();
+ first.click();
+ $rootScope.$digest();
+ expect($rootScope.currentPage).toBe(1);
+ });
+
+ it('does not change the current page on "last" click if already at last page', function() {
+ var last = element.find('li').eq(-1).find('a').eq(0);
+ $rootScope.currentPage = $rootScope.numPages;
+ $rootScope.$digest();
+ last.click();
+ $rootScope.$digest();
+ expect($rootScope.currentPage).toBe($rootScope.numPages);
+ });
+
+ it('changes "first" & "last" text from attributes', function() {
+ element = $compile('')($rootScope);
+ $rootScope.$digest();
+
+ expect(element.find('li').eq(0).text()).toBe('<<<');
+ expect(element.find('li').eq(-1).text()).toBe('>>>');
+ });
+
+ it('changes "previous" & "next" text from attributes', function() {
+ element = $compile('')($rootScope);
+ $rootScope.$digest();
+
+ expect(element.find('li').eq(1).text()).toBe('<<');
+ expect(element.find('li').eq(-2).text()).toBe('>>');
+ });
+
+});
+
+describe('pagination directive with just number links', function () {
+ var $rootScope, element;
+ beforeEach(module('ui.bootstrap.pagination'));
+ beforeEach(module('template/pagination/pagination.html'));
+ beforeEach(inject(function(_$compile_, _$rootScope_) {
+ $compile = _$compile_;
+ $rootScope = _$rootScope_;
+ $rootScope.numPages = 5;
+ $rootScope.currentPage = 3;
+ element = $compile('')($rootScope);
+ $rootScope.$digest();
+ }));
+
+ it('has a "pagination" css class', function() {
+ expect(element.hasClass('pagination')).toBe(true);
+ });
+
+ it('contains one ul and num-pages li elements', function() {
+ expect(element.find('ul').length).toBe(1);
+ expect(element.find('li').length).toBe(5);
+ expect(element.find('li').eq(0).text()).toBe('1');
+ expect(element.find('li').eq(-1).text()).toBe(''+$rootScope.numPages);
+ });
+
+ it('has the number of the page as text in each page item', function() {
+ var lis = element.find('li');
+ for(var i=0; i<$rootScope.numPages;i++) {
+ expect(lis.eq(i).text()).toEqual(''+(i+1));
+ }
+ });
+
+ it('sets the current-page to be active', function() {
+ var currentPageItem = element.find('li').eq($rootScope.currentPage-1);
+ expect(currentPageItem.hasClass('active')).toBe(true);
+ });
+
+ it('does not disable the "1" link if current-page is 1', function() {
+ $rootScope.currentPage = 1;
+ $rootScope.$digest();
+ var onePageItem = element.find('li').eq(0);
+ expect(onePageItem.hasClass('disabled')).toBe(false);
+ expect(onePageItem.hasClass('active')).toBe(true);
+ });
+
+ it('does not disable the "numPages" link if current-page is num-pages', function() {
+ $rootScope.currentPage = 5;
+ $rootScope.$digest();
+ var lastPageItem = element.find('li').eq(-1);
+ expect(lastPageItem.hasClass('disabled')).toBe(false);
+ expect(lastPageItem.hasClass('active')).toBe(true);
+ });
+
+ it('changes currentPage if a page link is clicked', function() {
+ var page2 = element.find('li').eq(1).find('a');
+ page2.click();
+ $rootScope.$digest();
+ expect($rootScope.currentPage).toBe(2);
+ });
+
+
+ it('executes the onSelectPage expression when the current page changes', function() {
+ $rootScope.selectPageHandler = jasmine.createSpy('selectPageHandler');
+ element = $compile('')($rootScope);
+ $rootScope.$digest();
+ var page2 = element.find('li').eq(1).find('a').eq(0);
+ page2.click();
+ $rootScope.$digest();
+ expect($rootScope.selectPageHandler).toHaveBeenCalledWith(2);
+ });
+
+ it('changes the number of items when numPages changes', function() {
+ $rootScope.numPages = 8;
+ $rootScope.$digest();
+ expect(element.find('li').length).toBe(8);
+ expect(element.find('li').eq(0).text()).toBe('1');
+ expect(element.find('li').eq(-1).text()).toBe(''+$rootScope.numPages);
+ });
+
+ it('sets the current page to the last page if the numPages is changed to less than the current page', function() {
+ $rootScope.selectPageHandler = jasmine.createSpy('selectPageHandler');
+ element = $compile('')($rootScope);
+ $rootScope.$digest();
+ $rootScope.numPages = 2;
+ $rootScope.$digest();
+ expect(element.find('li').length).toBe(2);
+ expect($rootScope.currentPage).toBe(2);
+ expect($rootScope.selectPageHandler).toHaveBeenCalledWith(2);
+ });
+});
+
+describe('setting paginationConfig', function() {
+ var $rootScope, element;
+ var originalConfig = {};
+ beforeEach(module('ui.bootstrap.pagination'));
+ beforeEach(module('template/pagination/pagination.html'));
+ beforeEach(inject(function(_$compile_, _$rootScope_, paginationConfig) {
+ $compile = _$compile_;
+ $rootScope = _$rootScope_;
+ $rootScope.numPages = 5;
+ $rootScope.currentPage = 3;
+ angular.extend(originalConfig, paginationConfig);
+ paginationConfig.boundaryLinks = true;
+ paginationConfig.directionLinks = true;
+ paginationConfig.firstText = 'FI';
+ paginationConfig.previousText = 'PR';
+ paginationConfig.nextText = 'NE';
+ paginationConfig.lastText = 'LA';
+ element = $compile('')($rootScope);
+ $rootScope.$digest();
+ }));
+ afterEach(inject(function(paginationConfig) {
+ // return it to the original state
+ angular.extend(paginationConfig, originalConfig);
+ }));
+
+ it('should change paging text', function () {
+ expect(element.find('li').eq(0).text()).toBe('FI');
+ expect(element.find('li').eq(1).text()).toBe('PR');
+ expect(element.find('li').eq(-2).text()).toBe('NE');
+ expect(element.find('li').eq(-1).text()).toBe('LA');
+ });
+
+ it('contains one ul and num-pages + 4 li elements', function() {
+ expect(element.find('ul').length).toBe(1);
+ expect(element.find('li').length).toBe(9);
+ });
+
+});
+
+
+describe('pagination directive with first, last & number links', function () {
+ var $rootScope, element;
+ beforeEach(module('ui.bootstrap.pagination'));
+ beforeEach(module('template/pagination/pagination.html'));
+ beforeEach(inject(function(_$compile_, _$rootScope_) {
+ $compile = _$compile_;
+ $rootScope = _$rootScope_;
+ $rootScope.numPages = 5;
+ $rootScope.currentPage = 3;
+ element = $compile('')($rootScope);
+ $rootScope.$digest();
+ }));
+
+
+ it('contains one ul and num-pages + 2 li elements', function() {
+ expect(element.find('ul').length).toBe(1);
+ expect(element.find('li').length).toBe(7);
+ expect(element.find('li').eq(0).text()).toBe('First');
+ expect(element.find('li').eq(1).text()).toBe('1');
+ expect(element.find('li').eq(-2).text()).toBe(''+$rootScope.numPages);
+ expect(element.find('li').eq(-1).text()).toBe('Last');
+ });
+
+
+ it('disables the "first" & activates "1" link if current-page is 1', function() {
+ $rootScope.currentPage = 1;
+ $rootScope.$digest();
+ expect(element.find('li').eq(0).hasClass('disabled')).toBe(true);
+ expect(element.find('li').eq(1).hasClass('disabled')).toBe(false);
+ expect(element.find('li').eq(1).hasClass('active')).toBe(true);
+ });
+
+ it('disables the "last" & "next" link if current-page is num-pages', function() {
+ $rootScope.currentPage = 5;
+ $rootScope.$digest();
+ expect(element.find('li').eq(-2).hasClass('disabled')).toBe(false);
+ expect(element.find('li').eq(-2).hasClass('active')).toBe(true);
+ expect(element.find('li').eq(-1).hasClass('disabled')).toBe(true);
+ });
+
+
+ it('changes currentPage if the "first" link is clicked', function() {
+ var first = element.find('li').eq(0).find('a').eq(0);
+ first.click();
+ $rootScope.$digest();
+ expect($rootScope.currentPage).toBe(1);
+ });
+
+ it('changes currentPage if the "last" link is clicked', function() {
+ var last = element.find('li').eq(-1).find('a').eq(0);
+ last.click();
+ $rootScope.$digest();
+ expect($rootScope.currentPage).toBe($rootScope.numPages);
+ });
+
+});
+
+describe('pagination bypass configuration from attributes', function () {
+ var $rootScope, element;
+ beforeEach(module('ui.bootstrap.pagination'));
+ beforeEach(module('template/pagination/pagination.html'));
+ beforeEach(inject(function(_$compile_, _$rootScope_) {
+ $compile = _$compile_;
+ $rootScope = _$rootScope_;
+ $rootScope.numPages = 5;
+ $rootScope.currentPage = 3;
+ element = $compile('')($rootScope);
+ $rootScope.$digest();
+ }));
+
+ it('contains one ul and num-pages + 4 li elements', function() {
+ expect(element.find('ul').length).toBe(1);
+ expect(element.find('li').length).toBe(9);
+ });
+
it('should change paging text from attribute', function () {
expect(element.find('li').eq(0).text()).toBe('<<');
+ expect(element.find('li').eq(1).text()).toBe('<');
+ expect(element.find('li').eq(-2).text()).toBe('>');
expect(element.find('li').eq(-1).text()).toBe('>>');
});
diff --git a/template/pagination/pagination.html b/template/pagination/pagination.html
index 4c0d40baac..dec9c8f7cf 100644
--- a/template/pagination/pagination.html
+++ b/template/pagination/pagination.html
@@ -1,6 +1,4 @@