Creating a Pager with Knockout.js and Twitter Bootstrap

I struggled with this a bit, so I thought others might find this useful. This is a super easy way to build a data pager using KnockoutJS and Twitter Bootstrap. Here’s what the finished product will look like.

alt

First, let’s lay out our HTML. I’ve simplified the table a bit from what you see in the screenshot, but the basics are all there. We use the Twitter Bootstrap pagination classes and a bunch of Knockout data binding. It’s basically a “previous page” button and a “next page” button, with dynamic page numbers in between. A lot of the Knockout code is just doing checks to see if we should enable or disable certain buttons.

<table class="table table-striped table-bordered">  
   <tbody data-bind="foreach: pagedList">
        <tr>
            <td data-bind="text: name"></td>
            <td data-bind="text: shortName"></td>
            <td class="buttons">
                <a class="btn" data-bind="click: $root.edit" href="#" title="edit"><i class="icon-edit"></i></a>
                <a class="btn" data-bind="click: $root.remove" href="#" title="remove"><i class="icon-remove"></i></a>
            </td>
        </tr>
    </tbody>
</table>  
<div class="pagination">  
    <ul><li data-bind="css: { disabled: pageIndex() === 0 }"><a href="#" data-bind="click: previousPage">Previous</a></li></ul>
    <ul data-bind="foreach: allPages">
        <li data-bind="css: { active: $data.pageNumber === ($root.pageIndex() + 1) }"><a href="#" data-bind="text: $data.pageNumber, click: function() { $root.moveToPage($data.pageNumber-1); }"></a></li>
    </ul>
    <ul><li data-bind="css: { disabled: pageIndex() === maxPageIndex() }"><a href="#" data-bind="click: nextPage">Next</a></li></ul>
</div>  

Here’s what the pager should look like now:

alt

Now that we have our view set up, let’s add some knockout code to our ViewModel. Here is the barebones version. Again, I’ve cut out a lot of the cruft not related to the pager:

var ListViewModel = function (initialData) {  
    var self = this;
    window.viewModel = self;

    self.list = ko.observableArray(initialData);
    self.pageSize = ko.observable(10);
    self.pageIndex = ko.observable(0);

    self.pagedList = ko.dependentObservable(function () {
        var size = self.pageSize();
        var start = self.pageIndex() * size;
        return self.list.slice(start, start + size);
    });
    self.maxPageIndex = ko.dependentObservable(function () {
        return Math.ceil(self.list().length/self.pageSize())-1;
    });
    self.previousPage = function () {
        if (self.pageIndex() > 0) {
            self.pageIndex(self.pageIndex() - 1);
        }
    };
    self.nextPage = function () {
        if (self.pageIndex() < self.maxPageIndex()) {
            self.pageIndex(self.pageIndex() + 1);
        }
    };
    self.allPages = ko.dependentObservable(function () {
        var pages = [];
        for (i = 0; i <= self.maxPageIndex() ; i++) {
            pages.push({ pageNumber: (i + 1) });
        }
        return pages;
    });
    self.moveToPage = function (index) {
        self.pageIndex(index);
    };
};

And that’s that. This is not meant to work exactly as-is, but is rather a pattern that you can use to create a pretty simple data pager using Twitter Bootstrap and KnockoutJS.