Last active
August 29, 2015 14:01
-
-
Save begedin/4dc2ffc3052a7e9c171a to your computer and use it in GitHub Desktop.
Simple implementation of paging on a collection in knockout.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function Paging(itemSet) { | |
/// <summary>Component used to apply paging on a provided collection. Requires knockout.</summary> | |
/// <param name="itemSet" type="ko.observableArray or Array">Collection to apply the paging on.</param> | |
var self = this; | |
self.page = ko.observable(1); // currently active page, starts from 1 for increased "user friendliness" | |
self.itemsPerPage = ko.observable(20); // currently selected number of items per page | |
self.itemsPerPageOptions = [10, 20, 50, 100]; // available options for number of items per page | |
self.total = ko.computed(function () { | |
/// <summary>Counts total number of items in the currently assigned collection.</summary> | |
return itemSet().length; | |
}); | |
var totalPages = ko.computed(function () { | |
/// <summary>Returns total number of pages based on current collection size and page size setting</summary> | |
// how many full pages are there? | |
var maxPages = Math.floor(self.total() / parseInt(self.itemsPerPage(), 10)); | |
// is there an extra "not full" page | |
if ((self.total() % parseInt(self.itemsPerPage(), 10)) > 0) maxPages++; | |
// if there's 0 items in the collection, a single empty page still should be shown | |
return maxPages > 0 ? maxPages : 1; | |
}); | |
self.pageContent = ko.computed(function () { | |
/// <summary>Content of the current page. A total of <itemsPerPage> items</summary> | |
var startIndex = (self.page() - 1) * parseInt(self.itemsPerPage(), 10); | |
var endIndex = startIndex + parseInt(self.itemsPerPage(), 10); | |
// Filtered items do not need to be an observable array. | |
// A computed is in itself an observable. | |
var filteredItems = []; | |
var items = ko.utils.unwrapObservable(itemSet); | |
// The loop is only as long as a single page. | |
for (var i = startIndex; i < endIndex; i++) { | |
if (i < items.length) { | |
filteredItems.push(items[i]); | |
} else break; | |
} | |
return filteredItems; | |
}); | |
self.itemsPerPage.subscribe(function (newValue) { | |
// when the itemsPerPage option changes, we need to see if the current page is | |
// larger than the total number of pages for the new option value and set it to the new max in that case | |
if (self.page() > totalPages()) self.page(totalPages()); | |
}); | |
totalPages.subscribe(function (newValue) { | |
// when the total number of pages changes, we need to see if the current page | |
// is larger than the new total and set it to the new total in that case | |
if (self.page() > newValue) self.page(newValue); | |
}); | |
self.pageNavigation = ko.computed(function () { | |
/// <summary>Computes an array of "shortcuts" to navigate to specific page. The computed array is at most in range [currentPage - 4, currentPage + 5]</summary> | |
var pages = [], start = self.page() - 2; | |
if (start < 1) start = 1; | |
var end = start + 4; | |
if (end > totalPages()) end = totalPages(); | |
while (start <= end) { | |
pages.push(start++); | |
}; | |
return pages; | |
}); | |
self.setPage = function (index) { | |
self.page(index); | |
} | |
self.nextPage = function () { | |
/// <summary>Moves to next page</summary> | |
self.page(self.page() + 1); // no need to do checks, we enable/disable the control that calls the function instead | |
}; | |
self.previousPage = function () { | |
/// <summary>Moves to previous page</summary> | |
self.page(self.page() - 1); // no need to do checks, we enable/disable the control that calls the function instead | |
}; | |
self.firstPage = function () { | |
/// <summary>Jumps to first page</summary> | |
self.page(1); | |
}; | |
self.lastPage = function () { | |
/// <summary>Jumps to last page</summary> | |
self.page(totalPages()); | |
}; | |
self.isFirstPage = ko.computed(function () { | |
/// <summary>Check if the current page equals 1</summary> | |
return parseInt(self.page(), 10) === 1 ? true: false; | |
}); | |
self.isLastPage = ko.computed(function () { | |
/// <summary>Checks if current page equals total pages</summary> | |
if (self.page() === totalPages()) { return true; } | |
else { return false; } | |
}); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div class="pager-control"> | |
<input type="button" data-bind="click: paging.firstPage, enable: !paging.isFirstPage()" class="first" /> | |
<input type="button" data-bind="click: paging.previousPage, enable: !paging.isFirstPage()" class="previous" /> | |
<!-- ko foreach: paging.pageNavigation --> | |
<span class="page-number" data-bind="text: $data, css: { 'current-page': $data === $parent.paging.page() }, click: $parent.paging.setPage"></span> | |
<!-- /ko --> | |
<input type="button" data-bind="click: paging.nextPage, enable: !paging.isLastPage()" class="next" /> | |
<input type="button" data-bind="click: paging.lastPage, enable: !paging.isLastPage()" class="last" /> | |
<select data-bind="options: paging.itemsPerPageOptions, value: paging.itemsPerPage"></select> | |
</div> | |
<!-- to display the items, bind to "paging.pageContent"--> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* basic example of styling for the pager control */ | |
.pager-control { | |
display: inline-block; | |
vertical-align: middle; | |
line-height: 24px; | |
width: 100%; | |
position: absolute; | |
bottom: 0; | |
height: 30px; | |
background: white; | |
border-top: 2px solid #D9D6D5; | |
} | |
.pager-control input[type=button], .pager-control span, .pager-control select { | |
display: inline-block; | |
vertical-align: middle; | |
} | |
.pager-control select { | |
width: 60px; | |
text-align: center; | |
} | |
.pager-control input[type=button], .pager-control span.page-number { | |
height: 24px; | |
width: 24px; | |
} | |
.pager-control input[type=button] { | |
border: none; | |
} | |
.pager-control input[type=button]:disabled { | |
opacity: 0.5; | |
} | |
.pager-control span.page-number { | |
text-align: center; | |
line-height: 24px; | |
cursor: pointer; | |
} | |
.pager-control span.page-number.current-page{ | |
color: #FF8101; | |
} | |
.pager-control span.page-number:hover { | |
text-decoration: underline; | |
} | |
.pager-control input[type=button].first { | |
background: url(../Images/Paging_start24.png); | |
} | |
.pager-control input[type=button].last { | |
background: url(../Images/Paging_end24.png); | |
} | |
.pager-control input[type=button].previous { | |
background: url(../Images/Paging_prev24.png); | |
} | |
.pager-control input[type=button].next { | |
background: url(../Images/Paging_next24.png); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment