HTML Table Pagination using Knockout JS and ASP.NET Web API

Posted by: Mahesh Sabnis , on 2/11/2014, in Category ASP.NET
Views: 50714
Abstract: Use Knockout JS, ASP.NET Web API and ADO.NET Entity Framework to enable pagination in a HTML Table

Knockout JS is a simple yet powerful JavaScript library with a specific goal of enabling Model-View-ViewModel (MVVM) style for websites. The purpose is to use object classes to separate UI from business functions so that they become easier to test.

The question that arises is in what kind of scenarios can we use Knockout JS? Lets’ consider that an HTML page is to be created which makes a call to an external service (WCF Service/Web Service or ASP.NET WEB API) for fetching data and once the data is loaded in the HTML element (e.g. table), pagination is to be implemented. Now this can be done using any JavaScript library like jQuery. However there are some challenges to be thought of like how does the UI update when the underlying data model changes, how to store data on the client side which responds to the change notification for pagination (move previous, next etc.) and calls some logic. Another challenge could be how each user may display different number of rows initially when the page is loaded e.g. (4 rows/ 8 rows etc) and so on. Although these challenges can be coded with libraries like jQuery, it becomes easier if we choose a library that provides binding between UI and data model and has in-built change notification capability.

 

Knockout.js is the library which provides, Observable and ObservableArray model properties which can be used to bind with the UI (HTML elements) using data-bind attribute. With this useful feature, when the Observable and ObservableArray is changed, the corresponding UI element gets updated immediately. The Computed Observable is a function that is dependent on one or more Observables. The computed observable gets updated when the observable changes.

For the following application, ASP.NET MVC WEB API is used which makes use of ADO.NET EF to fetch data from SQL Server Database and EmployeeInfo table. The table schema is as below:

table-structure

Note: As per your technology comfort level, you can also use a WCF REST Service with JSON response format. The step-by-step walkthrough for creating WEB API application and using ADO.NET EF can be found at Simple Application using Knockout.js, jQuery and ASP.NET MVC 4.0 with WEB API.

Step 1: Open Visual Studio 2012/2013 and create a new project using ASP.NET Empty Web application template. In this project using NuGet Packages, add jQuery and Knockout.js scripts.

Step 2: In this project add a new HTML page and name it as ‘H1_KnockoutPagination.html’. In this page, add a new select element and table as shown here:

<div id="dvpaging">
<span>Items to be displayed in table:</span>
    <select id="lstpagrec">
        <option value="4">4</option>
        <option value="8">8</option>
        <option value="12">12</option>
        <option value="complete">Complete</option>
    </select>
</div>

<table id="tbldata">
<thead>
<tr>
      <th>EmpNo</th><th>EmpName</th><th>Salary</th><th>DeptName</th><th>Designation</th>
</tr>
</thead>
<tbody>
<tr>
     <td><span></span> </td>
    <td><span></span> </td>
    <td><span></span> </td>
    <td><span></span> </td>
    <td colspan="5"><span></span> </td>
</tr>
</tbody>
<tfoot>
<tr>
     <td>
          <nav>
              <a href="#" title="Previous">Previous</a>
              <a href="#" title="Next">Next</a>
          </nav>
     </td>
</tr>
</tfoot>
</table>

Also add below style on the page:

<style type="text/css">
    #tbldata,td {
     border:double;
    }
    th {
     border:solid;
     font-family:'Times New Roman';
     font-size:medium;
    }
</style>

Step 3: Add the script references on the page for jQuery and Knockout.js. You can use NuGet for the same.

Step 4: Now we need to implement the logic for:

  • Declaring Observable and ObservableArray for storing received data and for data binding with UI element.
  • Computed observable for displaying rows in the Table.
  • Functions for pagination.

In the body add the following JavaScript:

<script type="text/javascript">
var EmpViewModel = function () {
var self = this;

self.pagesize = ko.observable(4); // The Default size of the Table.
self.thispage = ko.observable(0); // The current Page.
self.pagineationEmp = ko.observableArray(); // The declaration for storing the Paginated data.
self.Employees = ko.observableArray([]); // The declaration holds data in it from the external call.

//The computed declaration for the number of display of records 

self.page = ko.computed(function () {
    //Logic for displaying number of rows in the table
    if (self.pagesize() == "complete") {
        self.pagineationEmp(self.Employees.slice(0));
    } else {
        var pgsize = parseInt(self.pagesize(), 10),
         fisrt = pgsize * self.thispage(),
         last = fisrt + pgsize;

        self.pagineationEmp(self.Employees.slice(fisrt, last));
    }

}, self);

//The function for the total number of pages
self.allpages = function () {
    var totpages = self.Employees().length / self.pagesize() || 1;
    return Math.ceil(totpages);
}

//The function for Next Page
self.nextpage = function () {
    if (self.thispage() < self.allpages() - 1) {
        self.thispage(this.thispage() + 1);
    }
}
//The function for Previous Page
self.previouspage = function () {
    if (self.thispage() > 0) {
        self.thispage(this.thispage() - 1);
    }
}

//The function to get data from external service (WEB API)
getemployees();
function getemployees()
{
    $.ajax({
        url: "http://localhost:16269/api/EmployeeInfoAPI",
        type: "GET",
        datatype: "json",
        contenttype:"application/json;utf-8"
    }).done(function (resp) {
        self.Employees(resp);
        alert("Success");
    }).fail(function (err) {
        alert("Error!! " + err.status +"  " + err.statusText);
    });
}
};
ko.applyBindings(new EmpViewModel());
</script>

The code does the following:

  1. Defines observables for Page Size, Current Page, Pagination and storing data after making call to external service.
  2. getemployees() function to make call to WEB API and receive Employee data from the external service.
  3. page is a  computed observable which is used decide the number of records to be displayed in the Table. This shows all records of the pagesize selected is complete otherwise based upon the selection of pagesize from UI, the number of records to be displayed gets changed.
  4. Allpages() function is used to calculate total number of pages for the table pagination. This is calculated by dividing the records length by the selected pagesize. The division rounding is done using Math.ceil() function.
  5. nextpage and previouspage, functions are used for the pagination.

Step 5: Once the logic is completed, we need to bind it with the UI as below:

- Bind the select with the pagesize as shown here:

<select id="lstpagrec" data-bind="value: pagesize">
    <option value="4">4</option>
    <option value="8">8</option>
    <option value="12">12</option>
    <option value="complete">Complete</option>
</select>

- Bind the table body with the paginationEmp and individual Employee properties as below:

<tbody data-bind="foreach: pagineationEmp">
    <tr>
         <td><span data-bind="text:EmpNo"></span> </td>
        <td><span data-bind="text: EmpName"></span> </td>
        <td><span data-bind="text: Salary"></span> </td>
        <td><span data-bind="text: DeptName"></span> </td>
        <td colspan="5"><span data-bind="text: Designation"></span> </td>
    </tr>
</tbody>

- Bind the hyperlink in the nav as below:

<nav>
  <a href="#" title="Previous" data-bind="click: previouspage">Previous</a>
  <a href="#" title="Next" data-bind="click:nextpage">Next</a>
</nav>

Step 6: Run the page in the browser and it will display as shown below:

knockout-paging

Here it shows 4 records by default, because 4 is the default selection from the select. Likewise using Previous and Next links, pagination can be implemented.

knockout-webapi

So as you can see, the knockout.js observable functionality makes it is very easy to create responsive applications.

Download the entire source code from our GitHub Repository

This article has been editorially reviewed by Suprotim Agarwal.

Absolutely Awesome Book on C# and .NET

C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.

We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).

Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.

Click here to Explore the Table of Contents or Download Sample Chapters!

What Others Are Reading!
Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+

Author
Mahesh Sabnis is a DotNetCurry author and a Microsoft MVP having over two decades of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions), and Front-end technologies like Angular and React. Follow him on twitter @maheshdotnet or connect with him on LinkedIn


Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!
Comment posted by olu on Monday, June 9, 2014 8:05 AM
Awesome
Comment posted by John on Monday, June 9, 2014 8:05 AM
Good stuff
Comment posted by Jeff on Tuesday, November 11, 2014 3:49 PM
Hey Mahesh - this looks promising, but I can't get it to work - I see that the "self.page" property is a computed, but nothing is referencing it? I've debugged and can't see where anyone is calling that property.   Am I missing something?
Comment posted by Jeff on Tuesday, November 11, 2014 3:59 PM
Hey Mahesh - one more thing - I see this "self.Employees.slice"...but self.Employees is an observable array - did you mean self.Employees().slice