Skip to content
This repository has been archived by the owner on May 29, 2019. It is now read-only.

Commit

Permalink
feat(pagination): add pager directive
Browse files Browse the repository at this point in the history
  • Loading branch information
bekos authored and pkozlowski-opensource committed Jun 21, 2013
1 parent 88c94ee commit d952647
Show file tree
Hide file tree
Showing 5 changed files with 270 additions and 16 deletions.
3 changes: 3 additions & 0 deletions src/pagination/docs/demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ <h4>Default</h4>
The selected page no: {{currentPage}}

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

<hr />
<h4>Limit the maximimum visible page-buttons</h4>
<pagination num-pages="bigNoOfPages" current-page="bigCurrentPage" max-size="maxSize" class="pagination-mini" boundary-links="true"></pagination>
<pagination rotate="false" num-pages="bigNoOfPages" current-page="bigCurrentPage" max-size="maxSize" class="pagination-mini" boundary-links="true"></pagination>
Expand Down
18 changes: 17 additions & 1 deletion src/pagination/docs/readme.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

A lightweight pagination directive that is focused on ... providing pagination & will take care of visualising a pagination bar and enable / disable buttons correctly!

### Settings ###
### Pagination Settings ###

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

Expand Down Expand Up @@ -49,3 +49,19 @@ Settings can be provided as attributes in the `<pagination>` or globally configu
_(Default: 'Last')_ :
Text for Last button.

### 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:

* `align`
_(Default: true)_ :
Whether to align each link to the sides.

* `previous-text`
_(Default: '« Previous')_ :
Text for Previous button.

* `next-text`
_(Default: 'Next »')_ :
Text for Next button.
89 changes: 74 additions & 15 deletions src/pagination/pagination.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
angular.module('ui.bootstrap.pagination', [])

.controller('PaginationController', ['$scope', function (scope) {

scope.noPrevious = function() {
return scope.currentPage === 1;
};
scope.noNext = function() {
return scope.currentPage === scope.numPages;
};

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

scope.selectPage = function(page) {
if ( ! scope.isActive(page) && page > 0 && page <= scope.numPages) {
scope.currentPage = page;
scope.onSelectPage({ page: page });
}
};
}])

.constant('paginationConfig', {
boundaryLinks: false,
directionLinks: true,
Expand All @@ -19,6 +40,7 @@ angular.module('ui.bootstrap.pagination', [])
maxSize: '=',
onSelectPage: '&'
},
controller: 'PaginationController',
templateUrl: 'template/pagination/pagination.html',
replace: true,
link: function(scope, element, attrs) {
Expand Down Expand Up @@ -111,22 +133,59 @@ angular.module('ui.bootstrap.pagination', [])
scope.selectPage(scope.numPages);
}
});
scope.noPrevious = function() {
return scope.currentPage === 1;
};
scope.noNext = function() {
return scope.currentPage === scope.numPages;
};
scope.isActive = function(page) {
return scope.currentPage === page;
};

scope.selectPage = function(page) {
if ( ! scope.isActive(page) && page > 0 && page <= scope.numPages) {
scope.currentPage = page;
scope.onSelectPage({ page: page });
}
};
}])

.constant('pagerConfig', {
previousText: '« Previous',
nextText: 'Next »',
align: true
})

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

// Setup configuration parameters
var previousText = angular.isDefined(attrs.previousText) ? scope.$parent.$eval(attrs.previousText) : config.previousText;
var nextText = angular.isDefined(attrs.nextText) ? scope.$parent.$eval(attrs.nextText) : config.nextText;
var align = angular.isDefined(attrs.align) ? scope.$parent.$eval(attrs.align) : config.align;

// Create page object used in template
function makePage(number, text, isDisabled, isPrevious, isNext) {
return {
number: number,
text: text,
disabled: isDisabled,
previous: ( align && isPrevious ),
next: ( align && isNext )
};
}

scope.$watch('numPages + currentPage', function() {
scope.pages = [];

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

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

if ( scope.currentPage > scope.numPages ) {
scope.selectPage(scope.numPages);
}
};
});
}
};
}]);
171 changes: 171 additions & 0 deletions src/pagination/test/pager.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
describe('pager directive with default configuration', function () {
var $rootScope, element;
beforeEach(module('ui.bootstrap.pagination'));
beforeEach(module('template/pagination/pager.html'));
beforeEach(inject(function(_$compile_, _$rootScope_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
$rootScope.numPages = 5;
$rootScope.currentPage = 3;
element = $compile('<pager num-pages="numPages" current-page="currentPage"></pager>')($rootScope);
$rootScope.$digest();
}));

it('has a "pager" css class', function() {
expect(element.hasClass('pager')).toBe(true);
});

it('contains 2 li elements', function() {
expect(element.find('li').length).toBe(2);
expect(element.find('li').eq(0).text()).toBe('« Previous');
expect(element.find('li').eq(-1).text()).toBe('Next »');
});

it('aligns previous & next page', function() {
expect(element.find('li').eq(0).hasClass('previous')).toBe(true);
expect(element.find('li').eq(0).hasClass('next')).toBe(false);

expect(element.find('li').eq(-1).hasClass('previous')).toBe(false);
expect(element.find('li').eq(-1).hasClass('next')).toBe(true);
});

it('disables the "previous" link if current-page is 1', function() {
$rootScope.currentPage = 1;
$rootScope.$digest();
expect(element.find('li').eq(0).hasClass('disabled')).toBe(true);
});

it('disables the "next" link if current-page is num-pages', function() {
$rootScope.currentPage = 5;
$rootScope.$digest();
expect(element.find('li').eq(-1).hasClass('disabled')).toBe(true);
});

it('changes currentPage if the "previous" link is clicked', function() {
var previous = element.find('li').eq(0).find('a').eq(0);
previous.click();
$rootScope.$digest();
expect($rootScope.currentPage).toBe(2);
});

it('changes currentPage if the "next" link is clicked', function() {
var next = element.find('li').eq(-1).find('a').eq(0);
next.click();
$rootScope.$digest();
expect($rootScope.currentPage).toBe(4);
});

it('does not change the current page on "previous" click if already at first page', function() {
var previous = element.find('li').eq(0).find('a').eq(0);
$rootScope.currentPage = 1;
$rootScope.$digest();
previous.click();
$rootScope.$digest();
expect($rootScope.currentPage).toBe(1);
});

it('does not change the current page on "next" click if already at last page', function() {
var next = element.find('li').eq(-1).find('a').eq(0);
$rootScope.currentPage = 5;
$rootScope.$digest();
next.click();
$rootScope.$digest();
expect($rootScope.currentPage).toBe(5);
});

it('executes the onSelectPage expression when the current page changes', function() {
$rootScope.selectPageHandler = jasmine.createSpy('selectPageHandler');
element = $compile('<pager num-pages="numPages" current-page="currentPage" on-select-page="selectPageHandler(page)"></pager>')($rootScope);
$rootScope.$digest();
var next = element.find('li').eq(-1).find('a').eq(0);
next.click();
$rootScope.$digest();
expect($rootScope.selectPageHandler).toHaveBeenCalledWith(4);
});

it('does not changes the number of items when numPages changes', function() {
$rootScope.numPages = 8;
$rootScope.$digest();
expect(element.find('li').length).toBe(2);
expect(element.find('li').eq(0).text()).toBe('« Previous');
expect(element.find('li').eq(-1).text()).toBe('Next »');
});

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('<pager num-pages="numPages" current-page="currentPage" on-select-page="selectPageHandler(page)"></pager>')($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 pagerConfig', function() {
var $rootScope, element;
var originalConfig = {};
beforeEach(module('ui.bootstrap.pagination'));
beforeEach(module('template/pagination/pager.html'));
beforeEach(inject(function(_$compile_, _$rootScope_, pagerConfig) {
$compile = _$compile_;
$rootScope = _$rootScope_;
$rootScope.numPages = 5;
$rootScope.currentPage = 3;
angular.extend(originalConfig, pagerConfig);
pagerConfig.previousText = 'PR';
pagerConfig.nextText = 'NE';
pagerConfig.align = false;
element = $compile('<pager num-pages="numPages" current-page="currentPage"></pager>')($rootScope);
$rootScope.$digest();
}));
afterEach(inject(function(pagerConfig) {
// return it to the original state
angular.extend(pagerConfig, originalConfig);
}));

it('contains 2 li elements', function() {
expect(element.find('li').length).toBe(2);
});

it('should change paging text', function () {
expect(element.find('li').eq(0).text()).toBe('PR');
expect(element.find('li').eq(-1).text()).toBe('NE');
});

it('should not align previous & next page link', function () {
expect(element.find('li').eq(0).hasClass('previous')).toBe(false);
expect(element.find('li').eq(-1).hasClass('next')).toBe(false);
});

});

describe('pagination bypass configuration from attributes', function () {
var $rootScope, element;
beforeEach(module('ui.bootstrap.pagination'));
beforeEach(module('template/pagination/pager.html'));
beforeEach(inject(function(_$compile_, _$rootScope_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
$rootScope.numPages = 5;
$rootScope.currentPage = 3;
element = $compile('<pager align="false" previous-text="\'<\'" next-text="\'>\'" num-pages="numPages" current-page="currentPage"></pager>')($rootScope);
$rootScope.$digest();
}));

it('contains 2 li elements', function() {
expect(element.find('li').length).toBe(2);
});

it('should change paging text from attributes', function () {
expect(element.find('li').eq(0).text()).toBe('<');
expect(element.find('li').eq(-1).text()).toBe('>');
});

it('should not align previous & next page link', function () {
expect(element.find('li').eq(0).hasClass('previous')).toBe(false);
expect(element.find('li').eq(-1).hasClass('next')).toBe(false);
});

});
5 changes: 5 additions & 0 deletions template/pagination/pager.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<div class="pager">
<ul>
<li ng-repeat="page in pages" ng-class="{disabled: page.disabled, previous: page.previous, next: page.next}"><a ng-click="selectPage(page.number)">{{page.text}}</a></li>
</ul>
</div>

0 comments on commit d952647

Please sign in to comment.