ng2-pagination
Advanced tools
Comparing version 0.0.1-alpha.13 to 0.0.1-beta.1
export { PaginatePipe } from './paginate-pipe'; | ||
export { PaginationService, IPaginationInstance } from './pagination-service'; | ||
export { PaginationControlsCmp } from './pagination-controls-cmp'; | ||
export { PAGINATION_DIRECTIVES } from './pagination-controls-cmp'; |
@@ -6,2 +6,2 @@ var paginate_pipe_1 = require('./paginate-pipe'); | ||
var pagination_controls_cmp_1 = require('./pagination-controls-cmp'); | ||
exports.PaginationControlsCmp = pagination_controls_cmp_1.PaginationControlsCmp; | ||
exports.PAGINATION_DIRECTIVES = pagination_controls_cmp_1.PAGINATION_DIRECTIVES; |
@@ -7,4 +7,11 @@ import { PaginationService } from "./pagination-service"; | ||
transform(collection: any[], args: any[]): any; | ||
/** | ||
* Create an IPaginationInstance object, using defaults for any optional properties not supplied. | ||
*/ | ||
private createInstance(collection, args); | ||
/** | ||
* Ensure the argument passed to the filter contains the required properties. | ||
*/ | ||
private checkConfig(config); | ||
/** | ||
* To avoid returning a brand new array each time the pipe is run, we store the state of the sliced | ||
@@ -17,5 +24,5 @@ * array for a given id. This means that the next time the pipe is run on this collection & id, we just | ||
/** | ||
* For a given id, returns true if the collection, start and end values are identical. | ||
* For a given id, returns true if the collection, size, start and end values are identical. | ||
*/ | ||
private stateIsIdentical(id, collection, start, end); | ||
} |
@@ -20,20 +20,25 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { | ||
PaginatePipe.prototype.transform = function (collection, args) { | ||
// for non-array types, throw an exception | ||
// When an observable is passed through the AsyncPipe, it will output | ||
// `null` until the subscription resolves. In this case, we want to | ||
// use the cached data from the `state` object to prevent the NgFor | ||
// from flashing empty until the real values arrive. | ||
if (!(collection instanceof Array)) { | ||
throw new Error("PaginationPipe: Argument error - expected an array, got " + typeof collection); | ||
var _id = args[0].id || this.service.defaultId; | ||
if (this.state[_id]) { | ||
return this.state[_id].slice; | ||
} | ||
else { | ||
return collection; | ||
} | ||
} | ||
var usingConfig = typeof args[0] === 'object'; | ||
var serverSideMode = usingConfig && args[0].totalItems !== undefined; | ||
var serverSideMode = args[0].totalItems !== undefined; | ||
var instance = this.createInstance(collection, args); | ||
var id = instance.id; | ||
var start, end; | ||
var perPage = instance.itemsPerPage; | ||
this.service.register(instance); | ||
if (!usingConfig && instance.totalItems !== collection.length) { | ||
this.service.setTotalItems(id, collection.length); | ||
} | ||
var itemsPerPage = instance.itemsPerPage; | ||
if (!serverSideMode && collection instanceof Array) { | ||
itemsPerPage = itemsPerPage || LARGE_NUMBER; | ||
start = (this.service.getCurrentPage(id) - 1) * itemsPerPage; | ||
end = start + itemsPerPage; | ||
perPage = perPage || LARGE_NUMBER; | ||
start = (instance.currentPage - 1) * perPage; | ||
end = start + perPage; | ||
var isIdentical = this.stateIsIdentical(id, collection, start, end); | ||
@@ -46,30 +51,33 @@ if (isIdentical) { | ||
this.saveState(id, collection, slice, start, end); | ||
this.service.change.emit(id); | ||
return slice; | ||
} | ||
} | ||
// save the state for server-side collection to avoid null | ||
// flash as new data loads. | ||
this.saveState(id, collection, collection, start, end); | ||
return collection; | ||
}; | ||
/** | ||
* Create an IPaginationInstance object, using defaults for any optional properties not supplied. | ||
*/ | ||
PaginatePipe.prototype.createInstance = function (collection, args) { | ||
var instance; | ||
if (typeof args[0] === 'string' || typeof args[0] === 'number') { | ||
var id = this.service.defaultId; | ||
instance = { | ||
id: id, | ||
itemsPerPage: parseInt(args[0]), | ||
currentPage: this.service.getCurrentPage(id) || 1, | ||
totalItems: collection.length | ||
}; | ||
var config = args[0]; | ||
this.checkConfig(config); | ||
return { | ||
id: config.id || this.service.defaultId, | ||
itemsPerPage: config.itemsPerPage || 0, | ||
currentPage: config.currentPage || 1, | ||
totalItems: config.totalItems || collection.length | ||
}; | ||
}; | ||
/** | ||
* Ensure the argument passed to the filter contains the required properties. | ||
*/ | ||
PaginatePipe.prototype.checkConfig = function (config) { | ||
var required = ['itemsPerPage', 'currentPage']; | ||
var missing = required.filter(function (prop) { return !config.hasOwnProperty(prop); }); | ||
if (0 < missing.length) { | ||
throw new Error("PaginatePipe: Argument is missing the following required properties: " + missing.join(', ')); | ||
} | ||
else if (typeof args[0] === 'object') { | ||
instance = { | ||
id: args[0].id || this.service.defaultId, | ||
itemsPerPage: args[0].itemsPerPage || 0, | ||
currentPage: args[0].currentPage, | ||
totalItems: args[0].totalItems || collection.length | ||
}; | ||
} | ||
else { | ||
throw new Error("PaginatePipe: Argument must be a string, number or an object. Got " + typeof args[0]); | ||
} | ||
return instance; | ||
}; | ||
@@ -85,2 +93,3 @@ /** | ||
collection: collection, | ||
size: collection.length, | ||
slice: slice, | ||
@@ -92,3 +101,3 @@ start: start, | ||
/** | ||
* For a given id, returns true if the collection, start and end values are identical. | ||
* For a given id, returns true if the collection, size, start and end values are identical. | ||
*/ | ||
@@ -100,3 +109,7 @@ PaginatePipe.prototype.stateIsIdentical = function (id, collection, start, end) { | ||
} | ||
return state.collection === collection && state.start === start && state.end === end; | ||
return state.collection === collection && | ||
state.size === collection.length && | ||
state.start === start && | ||
state.end === end; | ||
state.end === end; | ||
}; | ||
@@ -103,0 +116,0 @@ PaginatePipe = __decorate([ |
@@ -1,2 +0,2 @@ | ||
import { EventEmitter, ViewContainerRef } from 'angular2/core'; | ||
import { EventEmitter, ViewContainerRef, ChangeDetectorRef } from 'angular2/core'; | ||
import { PaginationService } from "./pagination-service"; | ||
@@ -7,23 +7,29 @@ export interface IPage { | ||
} | ||
export declare class PaginationControlsCmp { | ||
export declare class PaginationControlsBase { | ||
private service; | ||
private viewContainer; | ||
id: string; | ||
maxSize: number; | ||
directionLinks: boolean; | ||
autoHide: boolean; | ||
pageChange: EventEmitter<number>; | ||
customTemplate: any; | ||
private changeSub; | ||
pages: IPage[]; | ||
constructor(service: PaginationService, viewContainer: ViewContainerRef); | ||
private updatePages(); | ||
displayDefaultTemplate(): boolean; | ||
/** | ||
* Set up the subscription to the PaginationService.change observable. | ||
* The api object provides data and methods to be used in the template. | ||
* The reason it is done this way, rather than just using instance members, is so that we can | ||
* unify the way the component and directive templates access them. | ||
*/ | ||
private ngOnInit(); | ||
private ngAfterContentInit(); | ||
private ngOnChanges(); | ||
private ngOnDestroy(); | ||
api: { | ||
pages: any[]; | ||
directionLinks: boolean; | ||
autoHide: boolean; | ||
maxSize: number; | ||
getCurrent: () => number; | ||
setCurrent: (val: any) => void; | ||
previous: () => void; | ||
next: () => void; | ||
isFirstPage: () => boolean; | ||
isLastPage: () => boolean; | ||
}; | ||
private changeSub; | ||
constructor(service: PaginationService); | ||
updatePages(): void; | ||
ngOnInit(): void; | ||
ngOnChanges(): void; | ||
ngOnDestroy(): void; | ||
/** | ||
@@ -37,5 +43,12 @@ * Set the current page number. | ||
getCurrent(): number; | ||
isFirstPage(): boolean; | ||
isLastPage(): boolean; | ||
/** | ||
* Returns the last page number | ||
*/ | ||
getLastPage(): number; | ||
/** | ||
* Checks that the instance.currentPage property is within bounds for the current page range. | ||
* If not, return a correct value for currentPage, or the current value if OK. | ||
*/ | ||
private outOfBoundCorrection(instance); | ||
/** | ||
* Returns an array of IPage objects to use in the pagination controls. | ||
@@ -50,1 +63,24 @@ */ | ||
} | ||
export declare class PaginationControlsDirective extends PaginationControlsBase { | ||
private viewContainer; | ||
private cdr; | ||
id: string; | ||
maxSize: number; | ||
directionLinks: boolean; | ||
autoHide: boolean; | ||
pageChange: EventEmitter<number>; | ||
customTemplate: any; | ||
private templateView; | ||
constructor(service: PaginationService, viewContainer: ViewContainerRef, cdr: ChangeDetectorRef); | ||
ngOnInit(): void; | ||
ngAfterViewInit(): void; | ||
} | ||
export declare class PaginationControlsCmp extends PaginationControlsBase { | ||
id: string; | ||
maxSize: number; | ||
directionLinks: boolean; | ||
autoHide: boolean; | ||
pageChange: EventEmitter<number>; | ||
constructor(service: PaginationService); | ||
} | ||
export declare const PAGINATION_DIRECTIVES: (typeof PaginationControlsDirective | typeof PaginationControlsCmp)[]; |
@@ -0,1 +1,6 @@ | ||
var __extends = (this && this.__extends) || function (d, b) { | ||
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; | ||
function __() { this.constructor = d; } | ||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | ||
}; | ||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { | ||
@@ -11,18 +16,27 @@ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; | ||
var core_1 = require('angular2/core'); | ||
var common_1 = require('angular2/common'); | ||
var lang_1 = require('angular2/src/facade/lang'); | ||
var pagination_service_1 = require("./pagination-service"); | ||
var DEFAULT_TEMPLATE = "\n <ul class=\"pagination\" role=\"navigation\" aria-label=\"Pagination\" *ngIf=\"displayDefaultTemplate()\">\n\n <li class=\"pagination-previous\" [class.disabled]=\"isFirstPage()\" *ngIf=\"directionLinks\">\n <a *ngIf=\"1 < getCurrent()\" (click)=\"setCurrent(getCurrent() - 1)\" aria-label=\"Next page\">\n Previous <span class=\"show-for-sr\">page</span>\n </a>\n <span *ngIf=\"isFirstPage()\">Previous <span class=\"show-for-sr\">page</span></span>\n </li>\n\n <li [class.current]=\"getCurrent() === page.value\" *ngFor=\"#page of pages\">\n <a (click)=\"setCurrent(page.value)\" *ngIf=\"getCurrent() !== page.value\">\n <span class=\"show-for-sr\">Page</span>\n <span>{{ page.label }}</span>\n </a>\n <div *ngIf=\"getCurrent() === page.value\">\n <span class=\"show-for-sr\">You're on page</span>\n <span>{{ page.label }}</span>\n </div>\n </li>\n\n <li class=\"pagination-next\" [class.disabled]=\"isLastPage()\" *ngIf=\"directionLinks\">\n <a *ngIf=\"!isLastPage()\" (click)=\"setCurrent(getCurrent() + 1)\" aria-label=\"Next page\">\n Next <span class=\"show-for-sr\">page</span>\n </a>\n <span *ngIf=\"isLastPage()\">Next <span class=\"show-for-sr\">page</span></span>\n </li>\n\n </ul>\n "; | ||
function getTemplate() { | ||
return pagination_service_1.PaginationService.template || DEFAULT_TEMPLATE; | ||
} | ||
var PaginationControlsCmp = (function () { | ||
function PaginationControlsCmp(service, viewContainer) { | ||
var template_1 = require('./template'); | ||
var PaginationControlsBase = (function () { | ||
function PaginationControlsBase(service) { | ||
var _this = this; | ||
this.service = service; | ||
this.viewContainer = viewContainer; | ||
this.maxSize = 7; | ||
this.directionLinks = true; | ||
this.autoHide = false; | ||
this.pageChange = new core_1.EventEmitter(); | ||
this.pages = []; | ||
/** | ||
* The api object provides data and methods to be used in the template. | ||
* The reason it is done this way, rather than just using instance members, is so that we can | ||
* unify the way the component and directive templates access them. | ||
*/ | ||
this.api = { | ||
pages: [], | ||
directionLinks: true, | ||
autoHide: false, | ||
maxSize: 7, | ||
getCurrent: function () { return _this.getCurrent(); }, | ||
setCurrent: function (val) { return _this.setCurrent(val); }, | ||
previous: function () { return _this.setCurrent(_this.getCurrent() - 1); }, | ||
next: function () { return _this.setCurrent(_this.getCurrent() + 1); }, | ||
isFirstPage: function () { return _this.getCurrent() === 1; }, | ||
isLastPage: function () { return _this.getLastPage() === _this.getCurrent(); } | ||
}; | ||
this.changeSub = this.service.change | ||
@@ -35,16 +49,11 @@ .subscribe(function (id) { | ||
} | ||
PaginationControlsCmp.prototype.updatePages = function () { | ||
PaginationControlsBase.prototype.updatePages = function () { | ||
var inst = this.service.getInstance(this.id); | ||
this.pages = this.createPageArray(inst.currentPage, inst.itemsPerPage, inst.totalItems, this.maxSize); | ||
}; | ||
PaginationControlsCmp.prototype.displayDefaultTemplate = function () { | ||
if (this.customTemplate !== null) { | ||
return false; | ||
this.api.pages = this.createPageArray(inst.currentPage, inst.itemsPerPage, inst.totalItems, this.api.maxSize); | ||
var correctedCurrentPage = this.outOfBoundCorrection(inst); | ||
if (correctedCurrentPage !== inst.currentPage) { | ||
this.setCurrent(correctedCurrentPage); | ||
} | ||
return !this.autoHide || 1 < this.pages.length; | ||
}; | ||
/** | ||
* Set up the subscription to the PaginationService.change observable. | ||
*/ | ||
PaginationControlsCmp.prototype.ngOnInit = function () { | ||
PaginationControlsBase.prototype.ngOnInit = function () { | ||
if (this.id === undefined) { | ||
@@ -54,12 +63,6 @@ this.id = this.service.defaultId; | ||
}; | ||
PaginationControlsCmp.prototype.ngAfterContentInit = function () { | ||
if (this.customTemplate !== null) { | ||
this.viewContainer.createEmbeddedView(this.customTemplate); | ||
} | ||
}; | ||
PaginationControlsCmp.prototype.ngOnChanges = function () { | ||
PaginationControlsBase.prototype.ngOnChanges = function () { | ||
this.updatePages(); | ||
}; | ||
PaginationControlsCmp.prototype.ngOnDestroy = function () { | ||
// TODO: do i need to manually clean these up??? What's the difference between unsubscribe() and remove() | ||
PaginationControlsBase.prototype.ngOnDestroy = function () { | ||
this.changeSub.unsubscribe(); | ||
@@ -70,5 +73,4 @@ }; | ||
*/ | ||
PaginationControlsCmp.prototype.setCurrent = function (page) { | ||
this.service.setCurrentPage(this.id, page); | ||
this.pageChange.emit(this.service.getCurrentPage(this.id)); | ||
PaginationControlsBase.prototype.setCurrent = function (page) { | ||
this.pageChange.emit(page); | ||
}; | ||
@@ -78,16 +80,30 @@ /** | ||
*/ | ||
PaginationControlsCmp.prototype.getCurrent = function () { | ||
PaginationControlsBase.prototype.getCurrent = function () { | ||
return this.service.getCurrentPage(this.id); | ||
}; | ||
PaginationControlsCmp.prototype.isFirstPage = function () { | ||
return this.getCurrent() === 1; | ||
}; | ||
PaginationControlsCmp.prototype.isLastPage = function () { | ||
/** | ||
* Returns the last page number | ||
*/ | ||
PaginationControlsBase.prototype.getLastPage = function () { | ||
var inst = this.service.getInstance(this.id); | ||
return Math.ceil(inst.totalItems / inst.itemsPerPage) === inst.currentPage; | ||
return Math.ceil(inst.totalItems / inst.itemsPerPage); | ||
}; | ||
/** | ||
* Checks that the instance.currentPage property is within bounds for the current page range. | ||
* If not, return a correct value for currentPage, or the current value if OK. | ||
*/ | ||
PaginationControlsBase.prototype.outOfBoundCorrection = function (instance) { | ||
var totalPages = Math.ceil(instance.totalItems / instance.itemsPerPage); | ||
if (totalPages < instance.currentPage) { | ||
return totalPages; | ||
} | ||
else if (instance.currentPage < 1) { | ||
return 1; | ||
} | ||
return instance.currentPage; | ||
}; | ||
/** | ||
* Returns an array of IPage objects to use in the pagination controls. | ||
*/ | ||
PaginationControlsCmp.prototype.createPageArray = function (currentPage, itemsPerPage, totalItems, paginationRange) { | ||
PaginationControlsBase.prototype.createPageArray = function (currentPage, itemsPerPage, totalItems, paginationRange) { | ||
// paginationRange could be a string if passed from attribute, so cast to number. | ||
@@ -126,3 +142,3 @@ paginationRange = +paginationRange; | ||
*/ | ||
PaginationControlsCmp.prototype.calculatePageNumber = function (i, currentPage, paginationRange, totalPages) { | ||
PaginationControlsBase.prototype.calculatePageNumber = function (i, currentPage, paginationRange, totalPages) { | ||
var halfWay = Math.ceil(paginationRange / 2); | ||
@@ -150,36 +166,142 @@ if (i === paginationRange) { | ||
}; | ||
return PaginationControlsBase; | ||
})(); | ||
exports.PaginationControlsBase = PaginationControlsBase; | ||
var PaginationControlsDirective = (function (_super) { | ||
__extends(PaginationControlsDirective, _super); | ||
function PaginationControlsDirective(service, viewContainer, cdr) { | ||
_super.call(this, service); | ||
this.viewContainer = viewContainer; | ||
this.cdr = cdr; | ||
} | ||
Object.defineProperty(PaginationControlsDirective.prototype, "maxSize", { | ||
set: function (value) { | ||
this.api.maxSize = value; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(PaginationControlsDirective.prototype, "directionLinks", { | ||
set: function (value) { | ||
this.api.directionLinks = value; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(PaginationControlsDirective.prototype, "autoHide", { | ||
set: function (value) { | ||
this.api.autoHide = value; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
PaginationControlsDirective.prototype.ngOnInit = function () { | ||
// we need to detach the change detector initially, to prevent a | ||
// "changed after checked" error. | ||
this.cdr.detach(); | ||
}; | ||
PaginationControlsDirective.prototype.ngAfterViewInit = function () { | ||
var _this = this; | ||
if (this.customTemplate !== null) { | ||
this.templateView = this.viewContainer.createEmbeddedView(this.customTemplate); | ||
this.templateView.setLocal('paginationApi', this.api); | ||
} | ||
setTimeout(function () { return _this.cdr.reattach(); }); | ||
}; | ||
__decorate([ | ||
core_1.Input(), | ||
__metadata('design:type', String) | ||
], PaginationControlsCmp.prototype, "id", void 0); | ||
], PaginationControlsDirective.prototype, "id", void 0); | ||
__decorate([ | ||
core_1.Input(), | ||
__metadata('design:type', Number) | ||
], PaginationControlsCmp.prototype, "maxSize", void 0); | ||
__metadata('design:type', Number), | ||
__metadata('design:paramtypes', [Number]) | ||
], PaginationControlsDirective.prototype, "maxSize", null); | ||
__decorate([ | ||
core_1.Input(), | ||
__metadata('design:type', Boolean) | ||
], PaginationControlsCmp.prototype, "directionLinks", void 0); | ||
__metadata('design:type', Boolean), | ||
__metadata('design:paramtypes', [Boolean]) | ||
], PaginationControlsDirective.prototype, "directionLinks", null); | ||
__decorate([ | ||
core_1.Input(), | ||
__metadata('design:type', Boolean) | ||
], PaginationControlsCmp.prototype, "autoHide", void 0); | ||
__metadata('design:type', Boolean), | ||
__metadata('design:paramtypes', [Boolean]) | ||
], PaginationControlsDirective.prototype, "autoHide", null); | ||
__decorate([ | ||
core_1.Output(), | ||
__metadata('design:type', core_1.EventEmitter) | ||
], PaginationControlsCmp.prototype, "pageChange", void 0); | ||
], PaginationControlsDirective.prototype, "pageChange", void 0); | ||
__decorate([ | ||
core_1.ContentChild(core_1.TemplateRef), | ||
__metadata('design:type', Object) | ||
], PaginationControlsCmp.prototype, "customTemplate", void 0); | ||
], PaginationControlsDirective.prototype, "customTemplate", void 0); | ||
PaginationControlsDirective = __decorate([ | ||
core_1.Directive({ | ||
selector: '[paginationControls]' | ||
}), | ||
__metadata('design:paramtypes', [pagination_service_1.PaginationService, core_1.ViewContainerRef, core_1.ChangeDetectorRef]) | ||
], PaginationControlsDirective); | ||
return PaginationControlsDirective; | ||
})(PaginationControlsBase); | ||
exports.PaginationControlsDirective = PaginationControlsDirective; | ||
var PaginationControlsCmp = (function (_super) { | ||
__extends(PaginationControlsCmp, _super); | ||
function PaginationControlsCmp(service) { | ||
_super.call(this, service); | ||
} | ||
Object.defineProperty(PaginationControlsCmp.prototype, "maxSize", { | ||
set: function (value) { | ||
this.api.maxSize = value; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(PaginationControlsCmp.prototype, "directionLinks", { | ||
set: function (value) { | ||
this.api.directionLinks = value; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(PaginationControlsCmp.prototype, "autoHide", { | ||
set: function (value) { | ||
this.api.autoHide = value; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
__decorate([ | ||
core_1.Input(), | ||
__metadata('design:type', String) | ||
], PaginationControlsCmp.prototype, "id", void 0); | ||
__decorate([ | ||
core_1.Input(), | ||
__metadata('design:type', Number), | ||
__metadata('design:paramtypes', [Number]) | ||
], PaginationControlsCmp.prototype, "maxSize", null); | ||
__decorate([ | ||
core_1.Input(), | ||
__metadata('design:type', Boolean), | ||
__metadata('design:paramtypes', [Boolean]) | ||
], PaginationControlsCmp.prototype, "directionLinks", null); | ||
__decorate([ | ||
core_1.Input(), | ||
__metadata('design:type', Boolean), | ||
__metadata('design:paramtypes', [Boolean]) | ||
], PaginationControlsCmp.prototype, "autoHide", null); | ||
__decorate([ | ||
core_1.Output(), | ||
__metadata('design:type', core_1.EventEmitter) | ||
], PaginationControlsCmp.prototype, "pageChange", void 0); | ||
PaginationControlsCmp = __decorate([ | ||
core_1.Component({ | ||
selector: 'pagination-controls', | ||
template: getTemplate(), | ||
directives: [common_1.CORE_DIRECTIVES] | ||
template: template_1.DEFAULT_TEMPLATE, | ||
styles: [template_1.DEFAULT_STYLES] | ||
}), | ||
__metadata('design:paramtypes', [pagination_service_1.PaginationService, core_1.ViewContainerRef]) | ||
__metadata('design:paramtypes', [pagination_service_1.PaginationService]) | ||
], PaginationControlsCmp); | ||
return PaginationControlsCmp; | ||
})(); | ||
})(PaginationControlsBase); | ||
exports.PaginationControlsCmp = PaginationControlsCmp; | ||
exports.PAGINATION_DIRECTIVES = lang_1.CONST_EXPR([PaginationControlsDirective, PaginationControlsCmp]); |
@@ -31,4 +31,2 @@ import { EventEmitter } from 'angular2/core'; | ||
defaultId: string; | ||
static templateUrl: string; | ||
static template: string; | ||
register(instance: IPaginationInstance): void; | ||
@@ -35,0 +33,0 @@ /** |
@@ -90,2 +90,3 @@ var core_1 = require('angular2/core'); | ||
} | ||
return {}; | ||
}; | ||
@@ -92,0 +93,0 @@ /** |
{ | ||
"name": "ng2-pagination", | ||
"version": "0.0.1-alpha.13", | ||
"version": "0.0.1-beta.1", | ||
"description": "Pagination for Angular2", | ||
@@ -8,3 +8,6 @@ "main": "index.js", | ||
"build": "node node_modules/typescript/bin/tsc", | ||
"test": "node node_modules/karma/bin/karma start karma.conf.js" | ||
"test": "karma start karma.conf.js", | ||
"demo:watch": "webpack --progress --colors --watch", | ||
"demo:dist": "webpack --progress --colors --dist", | ||
"publish": "npm run build && npm run demo:dist" | ||
}, | ||
@@ -30,5 +33,8 @@ "files": [ | ||
"devDependencies": { | ||
"angular2": "^2.0.0-beta.0", | ||
"angular2": "^2.0.0-beta.6", | ||
"bulma": "^0.0.14", | ||
"es6-promise": "^3.0.2", | ||
"es6-shim": "^0.33.13", | ||
"highlight.js": "^9.1.0", | ||
"html-loader": "^0.4.3", | ||
"jasmine-core": "^2.4.1", | ||
@@ -46,4 +52,3 @@ "karma": "^0.13.15", | ||
}, | ||
"dependencies": { | ||
} | ||
"dependencies": {} | ||
} |
201
readme.md
# Angular2 Pagination | ||
This is a **work-in-progress** port of my [angular-utils-pagination](https://github.com/michaelbromley/angularUtils/tree/master/src/directives/pagination) | ||
module from Angular 1.x to Angular 2. | ||
This is a port of my [angular-utils-pagination](https://github.com/michaelbromley/angularUtils/tree/master/src/directives/pagination) | ||
module from Angular 1.x to Angular 2. Due to fundamental differences in the design of Angular2, the API is different but | ||
the idea is the same - the most simple possible way to add full-featured pagination to an Angular app. | ||
It is not yet well production-tested and is currently missing a few features, but I hope to bring it eventually to full | ||
feature-parity with the 1.x version. | ||
The API will not quite be the same, but it should be significantly simpler and easier to develop than the 1.x version (I hope). | ||
## Demo | ||
@@ -15,9 +11,6 @@ | ||
To play with the demo, just clone this repo and start editing the files in the `/demo` folder. The index for the demo is | ||
`index.html` in the root folder. | ||
## Quick Start | ||
## Installation | ||
Requirements: TypeScript 1.6+ (for TS builds), tested with Angular 2.0.0-beta.6+ | ||
I am currently working with TypeScript 1.7.3, so these instructions may not work with earlier versions. | ||
``` | ||
@@ -27,8 +20,8 @@ npm install ng2-pagination --save | ||
**Note** that currently this module only supports commonjs. | ||
## Example of usage | ||
## Simple Example | ||
```TypeScript | ||
import {Component} from 'angular2/core'; | ||
import {CORE_DIRECTIVES} from 'angular2/common'; | ||
import {PaginatePipe, PaginationControlsCmp, PaginationService} from 'ng2-pagination'; | ||
@@ -40,8 +33,8 @@ | ||
<ul> | ||
<li *ngFor="#item of collection | paginate: [ITEMS_PER_PAGE or CONFIG_OBJECT] "> ... </li> | ||
<li *ngFor="#item of collection | paginate: { itemsPerPage: 10, currentPage: p }"> ... </li> | ||
<ul> | ||
<pagination-controls></pagination-controls> | ||
<pagination-controls (pageChange)="p = $event"></pagination-controls> | ||
`, | ||
directives: [CORE_DIRECTIVES, PaginationControlsCmp], | ||
directives: [PaginationControlsCmp], | ||
pipes: [PaginatePipe], | ||
@@ -52,12 +45,4 @@ providers: [PaginationService] | ||
public collection: any[]; | ||
public collection: any[] = someArrayOfThings; | ||
constructor(private dataService: DataService) { | ||
} | ||
ngInit() { | ||
this.dataService.getCollection() | ||
.subscribe(result => this.collection = result); | ||
} | ||
} | ||
@@ -70,35 +55,20 @@ ``` | ||
The PaginatePipe should be placed at the end of an NgFor expression. It accepts a single argument, which should be | ||
either a `number` or an object confirming to `IPaginationInstance`. If the argument is a number, the number sets the | ||
value of `itemsPerPage`. | ||
The PaginatePipe should be placed at the end of an NgFor expression. It accepts a single argument, an object confirming | ||
to `IPaginationInstance`. The following config options are available: | ||
Using an object allows some more advanced configuration. The following config options are available: | ||
```HTML | ||
<element *ngFor="#item of collection | paginate: { id: 'foo' | ||
itemsPerPage: pageSize | ||
currentPage: p | ||
totalItems: total }">...</element> | ||
```JavaScript | ||
interface IPaginationInstance { | ||
/** | ||
* An optional ID for the pagination instance. Only useful if you wish to | ||
* have more than once instance at a time in a given component. | ||
*/ | ||
id?: string; | ||
/** | ||
* The number of items per paginated page. | ||
*/ | ||
itemsPerPage: number; | ||
/** | ||
* The current (active) page. | ||
*/ | ||
currentPage: number; | ||
/** | ||
* The total number of items in the collection. Only useful when | ||
* doing server-side paging, where the collection size is limited | ||
* to a single page returned by the server API. | ||
* | ||
* For in-memory paging, this property should not be set, as it | ||
* will be automatically set to the value of collection.length. | ||
*/ | ||
totalItems?: number; | ||
} | ||
``` | ||
* **`itemsPerPage`** [`number` - **required**] The number of items to display on each page. | ||
* **`currentPage`** [`number` - **required**] The current (active) page number. | ||
* **`id`** [`string`] If you need to support more than one instance of pagination at a time, set the `id` and ensure it | ||
matches the id set in the PaginatePipe config (see below). | ||
* **`totalItems`** [`number`] The total number of items in the collection. Only useful when doing server-side paging, | ||
where the collection size is limited to a single page returned by the server API. For in-memory paging, | ||
this property should not be set, as it will be automatically set to the value of collection.length. | ||
@@ -109,4 +79,5 @@ ### PaginationControlsCmp | ||
<pagination-controls id="some_id" | ||
(change)="pageChanged($event)" | ||
(pageChange)="pageChanged($event)" | ||
maxSize="9" | ||
directionLinks="true" | ||
autoHide="true"> | ||
@@ -116,32 +87,112 @@ </pagination-controls> | ||
* **`id`** [string] If you need to support more than one instance of pagination at a time, set the `id` and ensure it | ||
* **`id`** [`string`] If you need to support more than one instance of pagination at a time, set the `id` and ensure it | ||
matches the id set in the PaginatePipe config. | ||
* **`change`** [function] The function specified will be invoked whenever the page changes via a click on one of the | ||
pagination controls. The `$event` argument will be the number of the new page. | ||
* **maxSize** [number] Defines the maximum number of page links to display. Default is `7`. | ||
* **`autoHide`** [boolean] If set to `true`, the pagination controls will not be displayed when all items in the | ||
collection fit onto the first page. Default is `false` | ||
* **`pageChange`** [`event handler`] The expression specified will be invoked whenever the page changes via a click on one of the | ||
pagination controls. The `$event` argument will be the number of the new page. This should be used to update the value of | ||
the `currentPage` variable which was passed to the `PaginatePipe`. | ||
* **`maxSize`** [`number`] Defines the maximum number of page links to display. Default is `7`. | ||
* **`directionLinks`** [`boolean`] If set to `false`, the "previous" and "next" links will not be displayed. Default is `true`. | ||
* **`autoHide`** [`boolean`] If set to `true`, the pagination controls will not be displayed when all items in the | ||
collection fit onto the first page. Default is `false`. | ||
## To Do | ||
## Server-Side Paging | ||
The following features from the 1.x version have not yet been implemented: | ||
In many cases - for example when working with very large data-sets - we do not want to work with the full collection | ||
in memory, and use some kind of server-side paging, where the server sends just a single page at a time. | ||
- ~~Handling of multiple independent instances within the same component.~~ | ||
- ~~Server-side paging support~~ | ||
- ~~Page-change event support~~ | ||
- Custom template support | ||
- ~~Various style options for the pagination controls template~~ | ||
- Full test suite | ||
This scenario is supported by ng2-pagination by using the `totalItems` config option. | ||
I have written a few tests, but right now I am waiting for the Angular team to publish some guidelines on how | ||
best to test components. | ||
Given a server response json object like this: | ||
``` | ||
{ | ||
"count": 14453, | ||
"data": [ | ||
{ /* item 1 */ }, | ||
{ /* item 2 */ }, | ||
{ /* item 3 */ }, | ||
{ /* ... */ }, | ||
{ /* item 10 */ } | ||
] | ||
} | ||
``` | ||
we should pass the value of `count` to the `PaginatePipe` as the `totalItems` argument: | ||
```HTML | ||
<li *ngFor="#item of collection | paginate: { itemsPerPage: 10, currentPage: p, totalItems: res.count }">...</li> | ||
``` | ||
This will allow the correct number of page links to be calculated. To see a complete example of this (including | ||
using the `async` pipe), see the [demo](http://michaelbromley.github.io/ng2-pagination/). | ||
## Custom Templates | ||
The `PaginationControlsCmp` component has a built-in default template and styles based on the [Foundation 6 pagination | ||
component](http://foundation.zurb.com/sites/docs/pagination.html). | ||
To use a custom template, you should instead use the `PaginationControlsDirective`: | ||
```HTML | ||
<template paginationControls #p="paginationApi" (pageChange)="currentPage = $event"> | ||
<div class="custom-pagination"> | ||
<div class="pagination-previous" [class.disabled]="p.isFirstPage()"> | ||
<a *ngIf="!p.isFirstPage()" (click)="p.previous()"> < </a> | ||
</div> | ||
<div *ngFor="#page of p.pages" [class.current]="p.getCurrent() === page.value"> | ||
<a (click)="p.setCurrent(page.value)" *ngIf="p.getCurrent() !== page.value"> | ||
<span>{{ page.label }}</span> | ||
</a> | ||
<div *ngIf="p.getCurrent() === page.value"> | ||
<span>{{ page.label }}</span> | ||
</div> | ||
</div> | ||
<div class="pagination-next" [class.disabled]="p.isLastPage()" *ngIf="p.directionLinks"> | ||
<a *ngIf="!p.isLastPage()" (click)="p.next()"> > </a> | ||
</div> | ||
</div> | ||
</template> | ||
``` | ||
The key thing to note here is `#p="paginationApi` - this provides a local variable, `p`, which can be used in the | ||
template to access the `paginationApi` members, which are explained below: | ||
* **`pages`** [`{ label: string, value: any }[]`] Array of page objects containing the page number and label. | ||
* **`directionLinks`** [`boolean`] Corresponds to the value of `directionLinks` which is passed to the directive. | ||
* **`autoHide`** [`boolean`] Corresponds to the value of `autoHide` which is passed to the directive. | ||
* **`maxSize`** [`number`] Corresponds to the value of `maxSize` which is passed to the directive. | ||
* **`getCurrent()`** [`() => number`] Returns the current page number. | ||
* **`setCurrent(val)`** [`(val: number) => void`] Triggers the `pageChange` event with the page number passed as `val`. | ||
* **`previous()`** [`() -> void`] Sets current page to previous, triggering the `pageChange` event. | ||
* **`next()`** [`() -> void`] Sets current page to next, triggering the `pageChange` event. | ||
* **`isFirstPage()`** [`() => boolean`] Returns true if the current page is the first page. | ||
* **`isLastPage()`** [`() => boolean`] Returns true if the current page is the last page/ | ||
## Build | ||
To build, first run `npm install` to get all the dev dependencies. Then you can use the `npm run build` script to | ||
compile the TypeScript and generate the definition files. `npm run test` will fire up Karma with the Webpack | ||
plugin to run the tests. | ||
Requires globally-installed node, npm, typings. | ||
``` | ||
npm install | ||
typings install | ||
npm run test // Karma unit tests | ||
npm run demo:watch // Build the demo app and watch | ||
``` | ||
More complete contributing instructions to follow. | ||
## Dart Version | ||
For Dart users, there is a Dart port available here: https://github.com/laagland/ng2-dart-pagination. Note that this | ||
version was written and is maintained by a different author and may not be up-to-date with this repo. | ||
## License | ||
MIT |
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
40943
14
731
192
0
17