Właściwie pracuję nad stroną internetową, która ma wiele tabel (większość z nich wymaga stronicowania).
Tak naprawdę potrzebowałem jakiegoś reusable-component
do stronicowania, aby użyć go we wszystkich przypadkach, w których potrzebuję stronicowania.
Potrzebowałem także bardziej zaawansowanych funkcji niż te, które znalazły się w zaakceptowanej odpowiedzi na to pytanie.
Opracowałem więc własny komponent do rozwiązania tego problemu, oto on.
Now on Github
JsFiddle
i więcej szczegółów, kontynuować czytanie (Proszę zwrócić uwagę, aby wziąć kodu z GitHub, nie stąd, jak kod GitHub zostało zaktualizowane i rozszerzone, odkąd wstaw to tutaj)
JavaScript
function PagingVM(options) {
var self = this;
self.PageSize = ko.observable(options.pageSize);
self.CurrentPage = ko.observable(1);
self.TotalCount = ko.observable(options.totalCount);
self.PageCount = ko.pureComputed(function() {
return Math.ceil(self.TotalCount()/self.PageSize());
});
self.SetCurrentPage = function (page) {
if (page < self.FirstPage)
page = self.FirstPage;
if (page > self.LastPage())
page = self.LastPage();
self.CurrentPage(page);
};
self.FirstPage = 1;
self.LastPage = ko.pureComputed(function() {
return self.PageCount();
});
self.NextPage = ko.pureComputed(function() {
var next = self.CurrentPage() + 1;
if (next > self.LastPage())
return null;
return next;
});
self.PreviousPage = ko.pureComputed(function() {
var previous = self.CurrentPage() - 1;
if (previous < self.FirstPage)
return null;
return previous;
});
self.NeedPaging = ko.pureComputed(function() {
return self.PageCount() > 1;
});
self.NextPageActive = ko.pureComputed(function() {
return self.NextPage() != null;
});
self.PreviousPageActive = ko.pureComputed(function() {
return self.PreviousPage() != null;
});
self.LastPageActive = ko.pureComputed(function() {
return (self.LastPage() != self.CurrentPage());
});
self.FirstPageActive = ko.pureComputed(function() {
return (self.FirstPage != self.CurrentPage());
});
// this should be odd number always
var maxPageCount = 7;
self.generateAllPages = function() {
var pages = [];
for (var i = self.FirstPage; i <= self.LastPage() ; i++)
pages.push(i);
return pages;
};
self.generateMaxPage = function() {
var current = self.CurrentPage();
var pageCount = self.PageCount();
var first = self.FirstPage;
var upperLimit = current + parseInt((maxPageCount - 1)/2);
var downLimit = current - parseInt((maxPageCount - 1)/2);
while (upperLimit > pageCount) {
upperLimit--;
if (downLimit > first)
downLimit--;
}
while (downLimit < first) {
downLimit++;
if (upperLimit < pageCount)
upperLimit++;
}
var pages = [];
for (var i = downLimit; i <= upperLimit; i++) {
pages.push(i);
}
return pages;
};
self.GetPages = ko.pureComputed(function() {
self.CurrentPage();
self.TotalCount();
if (self.PageCount() <= maxPageCount) {
return ko.observableArray(self.generateAllPages());
} else {
return ko.observableArray(self.generateMaxPage());
}
});
self.Update = function (e) {
self.TotalCount(e.TotalCount);
self.PageSize(e.PageSize);
self.SetCurrentPage(e.CurrentPage);
};
self.GoToPage = function (page) {
if (page >= self.FirstPage && page <= self.LastPage())
self.SetCurrentPage(page);
}
self.GoToFirst = function() {
self.SetCurrentPage(self.FirstPage);
};
self.GoToPrevious = function() {
var previous = self.PreviousPage();
if (previous != null)
self.SetCurrentPage(previous);
};
self.GoToNext = function() {
var next = self.NextPage();
if (next != null)
self.SetCurrentPage(next);
};
self.GoToLast = function() {
self.SetCurrentPage(self.LastPage());
};
}
HTML
<ul data-bind="visible: NeedPaging" class="pagination pagination-sm">
<li data-bind="css: { disabled: !FirstPageActive() }">
<a data-bind="click: GoToFirst">First</a>
</li>
<li data-bind="css: { disabled: !PreviousPageActive() }">
<a data-bind="click: GoToPrevious">Previous</a>
</li>
<!-- ko foreach: GetPages() -->
<li data-bind="css: { active: $parent.CurrentPage() === $data }">
<a data-bind="click: $parent.GoToPage, text: $data"></a>
</li>
<!-- /ko -->
<li data-bind="css: { disabled: !NextPageActive() }">
<a data-bind="click: GoToNext">Next</a>
</li>
<li data-bind="css: { disabled: !LastPageActive() }">
<a data-bind="click: GoToLast">Last</a>
</li>
</ul>
Cechy
Pokaż na potrzeby
gdy nie ma potrzeby przywoływania w ogóle (na przykład elementów, które trzeba wyświetlić mniej niż rozmiar strony), wtedy komponent HTML
wyłączy się zjawić się.
Zostanie to ustalone na podstawie oświadczenia data-bind="visible: NeedPaging"
.
Wyłącz na potrzeby
na przykład, jeśli jesteś już wybrany ostatnią stronę, dlaczego last page
lub przycisk Next
powinny być dostępne dla prasy?
ja operowania tym iw tym przypadku jestem wyłączając te przyciski stosując następujące wiązaniadata-bind="css: { disabled: !PreviousPageActive() }"
rozróżnić wybranej strony
specjalnej klasy (w tym przypadku o nazwie active
klasa) jest stosowana na wybranej stronie, aby użytkownik wiedział, na której stronie jest teraz.
ten jest ustalany przez wiązania data-bind="css: { active: $parent.CurrentPage() === $data }"
Ostatni & Pierwszy
przechodząc do pierwszej i ostatniej stronie jest również dostępna za pomocą prostych przycisków dedykowanych do tego.
Ograniczenia wyświetlanych przycisków
Załóżmy, że masz dużo stron, na przykład, 1000 stron, to co się stanie? czy wyświetlałbyś je wszystkie dla użytkownika? Absolutnie nie musisz wyświetlić tylko kilka z nich zgodnie z aktualną stroną. na przykład, pokazując 3 strony przed i inne 3 strony po wybranej stronie.
Ta sprawa została obsłużona tutaj <!-- ko foreach: GetPages() -->
Funkcja stosująca prosty algorytm do określenia, czy musimy wyświetlić wszystkie strony (liczba stron jest poniżej progu, co można łatwo określić), lub pokazać tylko niektóre z nich przyciski.
można określić wartość progową, zmieniając wartość zmiennej maxPageCount
W tej chwili przypisałem ją do następującej var maxPageCount = 7;
, co oznacza, że nie można wyświetlić więcej niż 7 przycisków dla użytkownika (3 przed SelectedPage i 3 po Wybrana strona) i sama wybrana strona.
Możesz się zastanawiać, co by było, gdyby nie było wystarczającej ilości stron po lub przed wyświetleniem bieżącej strony? nie martw się ja obchodzenia tego w algorytmie
na przykład, jeśli masz 11 pages
i masz maxPageCount = 7
i aktualną selected page is 10
, wówczas Następujące strony zostaną pokazane
5,6,7,8,9,10(selected page),11
więc zawsze stratyfikacji maxPageCount
, w poprzednim przykładzie pokazującym 5
stron przed wybraną stroną i tylko stronę 1
po wybranej stronie.
wybrana strona Validation
Wszystko zestaw operacji dla CurrentPage
obserwowalnym które określają wybraną stronę przez użytkownika, przeżywa funkcji SetCurrentPage
. Tylko w tej funkcji ustawiamy to obserwowalne i jak widać z kodu, przed ustawieniem wartości wykonujemy operacje sprawdzania poprawności, aby upewnić się, że nie przekroczymy dostępnej strony stron.
Już czystej
używam tylko pureComputed
nie computed
właściwości, co oznacza, że nie trzeba niepokoić się z oczyszczania i usuwania tych właściwości. Chociaż, jak widać na poniższym przykładzie, trzeba dysponować niektórych innych zapisów, które są poza składnika sam
UWAGA 1
Można zauważyć, że używam niektóre bootstrap
klasy w tym komponencie, To jest dla mnie odpowiednie, ale oczywiście , oczywiście, możesz używać własnych klas zamiast klas ładowania początkowego.
Klasy bootstrap które stosowane są tu pagination
, pagination-sm
, active
i disabled
Zapraszam je zmienić, jak trzeba.
UWAGA 2
Więc wprowadzono komponent dla ciebie, to jest czas, aby zobaczyć, jak to może działać.
Zintegrujesz ten komponent z głównym ViewModel w ten sposób.
function MainVM() {
var self = this;
self.PagingComponent = ko.observable(new Paging({
pageSize: 10, // how many items you would show in one page
totalCount: 100, // how many ALL the items do you have.
}));
self.currentPageSubscription = self.PagingComponent().CurrentPage.subscribe(function (newPage) {
// here is the code which will be executed when the user changes the page.
// you can handle this in the way you need.
// for example, in my case, I am requesting the data from the server again by making an ajax request
// and then updating the component
var data = /*bring data from server , for example*/
self.PagingComponent().Update({
// we need to set this again, why? because we could apply some other search criteria in the bringing data from the server,
// so the total count of all the items could change, and this will affect the paging
TotalCount: data.TotalCount,
// in most cases we will not change the PageSize after we bring data from the server
// but the component allows us to do that.
PageSize: self.PagingComponent().PageSize(),
// use this statement for now as it is, or you have to made some modifications on the 'Update' function.
CurrentPage: self.PagingComponent().CurrentPage(),
});
});
self.dispose = function() {
// you need to dispose the manual created subscription, you have created before.
self.currentPageSubscription.dispose();
}
}
Ostatni, ale nie najmniej, Oczywiście nie zapomnij zmienić wiązania w komponencie HTML zgodnie z tym szczególnym ViewModel lub owinąć cały element z with binding
like this
<div data-bind="with: PagingComponent()">
<!-- put the component here -->
</div>
Cheers
To jest świetne .. Dziękuję! Rozbudowałem go tak, aby wyświetlał każdy numer strony i pozwalał użytkownikowi na klikanie od strony 5, aby wypowiadać się na stronie 1. http://jsfiddle.net/LAbCv/31/ –
Zrzuca ostatnią połowę strony w prawo? 'Math.ceil' powinien znajdować się na stronie calc zamiast" Math.floor "? edytuj: Ahh dodajesz resztę/modulus z powrotem, po prostu skończę to i skończę. – MrYellow
Uważaj na to podejście, jeśli pracujesz z wszystkim, co może mieć spory wynik. Nie jest dobrym pomysłem załadowanie całej listy wyników po stronie klienta, a następnie po prostu strona w javascript. W przypadku dużych zestawów wyników potrzebujesz jakiegoś rozwiązania AJAX, które ładuje tylko dane wymagane dla bieżącej strony. – Mir