Thursday, February 20, 2014

Pagination with Knockout & JQuery

Oftern times, when we need to render large data sets on a web page, we must use pagination or something similar (think of twitter or facebook - load when scrolled down). Today, I'm going to show you how to create pagination with JQuery, Knockout.JS and a pretty cool jquery plugin, "Simple Pagination".

download the "Simple Pagination" JQuery plugin here. http://th3silverlining.com/2010/04/15/pajination-a-jquery-pagination-plugin/

Assume you have an API which return data something like this (orders in my case)

{
    "Items": [
      {
          "ID": 12, 
          "Description": "New pair of shoes", 
          "InvoiceDate": "2012-08-16T00:00:00" 
      },
      {
          "ID": 13, 
          "Description": "One bottle of Juise", 
          "InvoiceDate": "2012-09-16T00:00:00" 
      },
      [Upto 10 Items]
    ],
    "TotalCount": 38,
    "CurrentPage": 1,
    "PageSize": 10,
    "PageCount": 4
}

I have a ViewModel and a Model to match these data. Something like this... (javascript)

(function (itb) {
    var OrdersModel = function (id,desc,idate) {

        var self = this;

        self.ID = ko.observable(id);
        self.Description = ko.observable(desc);
        self.InvoiceDate = ko.observable(idate);
    };
    itb.OrdersModel = OrdersModel;
}(window.AwesomeSystem));

and a ViewModel for "business logic". Code is self explanatory, if don't understand, please mention in comments, I will try my best to explain.

(function (itb) {
    var OrdersViewModel = function () {

        var self = this;

        self.currentPage = ko.observable(1);
        self.pageCount = ko.observable(1);
        self.pageSize = ko.observable(10);
        self.totalCount = ko.observable(1);

        self.orders = ko.observableArray();

        self.loadSuccess = function (data) {

            // Clear the orders array
            self.orders.removeAll();

            // Set the pagining related data from JSON
            self.currentPage(data.CurrentPage);
            self.pageCount(data.PageCount);
            self.pageSize(data.pageSize);
            self.totalCount(data.TotalCount);

            // Loop through items in JSON, 
            // Create new order model and push it into the Items array
            // NOTE: you can use array map utilities in knockout too
            for (var i = 0; i < data.Items.length; i++) {
                var cu = data.Items[i];

                var newItem = new AwesomeSystem.OrdersModel(cu.ID, cu.Description, cu.InvoiceDate);

                self.orders.push(newItem);
            }

            // Lets create pagination for this data
            self.createPagination();
        };

        self.loadFailed = function (result) {
            alert("load failed");
        };

        self.initialize = function () {
            self.getOrdersAJAX();
        };

        // my api urs for paged data looks like
        // http://mysite/api/orders/{pageNo}/{pageSize}
        self.getOrdersAJAX = function (pageNo, pageSize) {
            var url = 'api/orders/';

            if (pageNo > 1)
                url += pageNo + "/" + pageSize;

            $.get(url, null, self.loadSuccess, self.loadFailed);
        };
        // load the given page 
        self.loadPage = function (pageNo, event) {
            console.log(pageNo);
            self.getOrdersAJAX(pageNo, self.pageSize());
        };

        self.createPagination = function () {
            // pagination source:  http://th3silverlining.com/2010/04/15/pajination-a-jquery-pagination-plugin/
            $("#paginationHolder").pagination({
                pages: self.pageCount(),
                itemsOnPage: self.pageSize(),
                currentPage: self.currentPage(),
                cssStyle: 'pagination',
                onPageClick: self.loadPage // fires when a page is clicked 
            });

        };

        self.initialize();
    };
    itb.OrdersViewModel = OrdersViewModel;
}(window.AwesomeSystem))

And, we have the Markup like this.

<table class="table table-bordered table-striped table-hover">
        <thead>
            <tr>
                <th>Description</th>
                <th>Invoice Date</th>
            </tr>
        </thead>
        <tbody data-bind="foreach: orders">

            <tr>
                <td>
                    <span data-bind="text: Description"></span>
                </td>
                <td>
                    <span data-bind="text: InvoiceDate"></span>
                </td>  
            </tr>

        </tbody>

    </table>
    <!-- // Table END -->
   <ul id="paginationHolder"></ul>

Lastly apply the bindings.

<script src="~/Scripts/plugins/jquery/jquery.simplePagination.js"></script>
<script>
    var viewModel = new AwesomeSystem.OrdersViewModel();
    ko.applyBindings(viewModel);
</script>

Voila!