I have a div that is setup to bind to a observeableArray
,but I only want to show at most 50 items from that observeableArray
at any given time. I want to handle this with pagination with a previous and next button along with indices on the page to allow users to cycle through pages of items from the collection.
I know I could probably do this with a computedObservable
and a custom data binding but I'm not sure how to do it (I'm still a Knockout neophyte).
Can anyone point me in the right direction?
Here is my code (the JS is in TypeScript):
<div class="container-fluid">
<div class="row-fluid">
<div class="span12">
<%=
if params[:q]
render 'active_search.html.erb'
else
render 'passive_search.html.erb'
end
%>
<%= form_tag("/search", method: "get", :class => "form-search form-inline") do %>
<%= label_tag(:q, "Search for:") %>
<%= text_field_tag(:q, nil, class:"input-medium search-query") %>
<%= submit_tag("Search", :class=>"btn") %>
<% end %>
<div class="media" data-bind="foreach: tweetsArray">
<%= image_tag('twitter-icon.svg', :class=>"tweet_img", :style=>"display:inline;") %>
<div class="media-body" style="display:inline;">
<h4 class="media-heading" data-bind="text: user.screen_name" style="display:inline;"></h4>
<span data-bind="text:text" style="display:inline;"></span> <br />
<span data-bind="text:'Created at '+created_at"></span> <br />
</div>
</div>
<div class="pagination pagination-centered">
<ul>
<li>
<a href="#">Prev</a>
</li>
<li>
<a href="#">1</a>
</li>
<li>
<a href="#">Next</a>
</li>
</ul>
</div>
</div>
</div>
</div>
<script>
var viewModel = new twitterResearch.TweetViewModel();
ko.applyBindings(viewModel);
//TODO: notes to self, use custom binding for pagination along with a computed observable to determine where at in the list you are
//document.onReady callback function
$(function() {
$.getJSON('twitter', {}, function(data) {
viewModel.pushTweet(data);
console.log(data.user);
});
});
</script>
declare var $: any;
declare var ko: any;
module twitterResearch {
class Tweet {
text: string;
created_at: string;
coordinates: string;
user: string;
entities: string;
id: number;
id_str: string;
constructor(_text: string, _created_at: string, _coordinates: any, _user: any,
_entities: any, _id_str: string, _id: number){
this.text = _text;
this.created_at = _created_at;
this.coordinates = _coordinates;
this.user = _user;
this.entities = _entities;
this.id_str = _id_str;
this.id = _id;
}
}
export class TweetViewModel{
tweetsArray: any;
constructor()
{
this.tweetsArray = ko.observableArray([]);
}
//tweet is going to be the JSON tweet we return
//from the server
pushTweet(tweet)
{
var _tweet = new Tweet(tweet.text, tweet.created_at, tweet.coordinates,
tweet.user, tweet.entities, tweet.id_str, tweet.id);
this.tweetsArray.push(_tweet);
this.tweetsArray.valueHasMutated();
}
}
}
Actually I am working on a website, which has a lot of tables (most of them need paging).
So actually, I needed some
reusable-component
for paging to use it in all the cases which I need paging.Also, I needed more advanced features than which provided in the accepted answer to this question.
So I developed my own component to solving this issue, here it is.
Now on Github
JsFiddle
And for more details, continue reading (Please consider to take the code from GitHub, not from here, as the GitHub code was updated and enhanced since I put it here)
JavaScript
HTML
Features
Show on need
When there is no need for paging at all (for example the items which need to display less than the page size) then the
HTML
component will disappear.This will be established by statement
data-bind="visible: NeedPaging"
.Disable on need
for example, if you are already selected the last page, why the
last page
or theNext
button should be available to press?I am handling this and in that case I am disabling those buttons by applying the following binding
data-bind="css: { disabled: !PreviousPageActive() }"
Distinguish the Selected page
a special class (in this case called
active
class) is applied on the selected page, to make the user know in which page he/she is right now.This is established by the binding
data-bind="css: { active: $parent.CurrentPage() === $data }"
Last & First
going to the first and last page is also available by simple buttons dedicated to this.
Limits for displayed buttons
suppose you have a lot of pages, for example, 1000 pages, then what will happen? would you display them all for the user? absolutely not you have to display just a few of them according to the current page. for example, showing 3 pages before and other 3 pages after the selected page.
This case has been handled here
<!-- ko foreach: GetPages() -->
the
GetPages
function applying a simple algorithm to determine if we need to show all the pages (the page count is under the threshold, which could be determined easily), or to show just some of the buttons.you can determine the threshold by changing the value of the
maxPageCount
variableRight now I assigned it as the following
var maxPageCount = 7;
which mean that no more than 7 buttons could be displayed for the user (3 before the SelectedPage, and 3 after the Selected Page) and the Selected Page itself.You may wonder, what if there were not enough pages after OR before the current page to display? do not worry I am handling this in the algorithm
for example, if you have
11 pages
and you havemaxPageCount = 7
and the currentselected page is 10
, Then the following pages will be shown5,6,7,8,9,10(selected page),11
so we always stratifying the
maxPageCount
, in the previous example showing5
pages before the selected page and just1
page after the selected page.Selected Page Validation
All set operation for the
CurrentPage
observable which determine the selected page by the user, is going through the functionSetCurrentPage
. In only this function we set this observable, and as you can see from the code, before setting the value we make validation operations to make sure that we will not go beyond the available page of the pages.Already clean
I use only
pureComputed
notcomputed
properties, which means you do not need to bother yourself with cleaning and disposing of those properties. Although, as you will see in the example below, you need to dispose of some other subscriptions which are outside of the component itselfNOTE 1
You may notice that I am using some
bootstrap
classes in this component, This is suitable for me, but , of course, you can use your own classes instead of the bootstrap classes.The bootstrap classes which I used here are
pagination
,pagination-sm
,active
anddisabled
Feel free to change them as you need.
NOTE 2
So I introduced the component for you, It is time to see how it could work.
You would integrate this component into your main ViewModel as like this.
Last but not least, Sure do not forget to change the binding in the HTML component according to your special viewModel, or wrap all the component with the
with binding
like thisCheers
Pagination is quite simple with Knockout. I would personally achieve it this way:
Given that, you can now add a function that increments (next) or decrements (previous) the current page.
Here is a quick example:
You'll find a simple and complete example here: http://jsfiddle.net/LAbCv/ (might be a bit buggy, but the idea is there).
I have created a blogpost with detailed explanation on how to create pagination with the help of a little JQuery plugin (here).
Basically, I have used normal knockout data binding with AJAX and after data has been retrieved from the server, I call the plugin. You can find the plugin here. It's called Simple Pagination.
This question is still one of the top searches for "knockout pagination", so the knockout extension knockout-paging (git) is worth mentioning.
It provides pagination by extending
ko.observableArray
. It is well documented and easy to use.The usage example is here.