jQWidgets SPA with Knockout.js and MongoDB Tutorial

In this guide, you will learn how to create a Single-page application featuring jQWidgets. A single-page application (SPA) is a web application or web site that fits on a single web page with the goal of providing a more fluid user experience akin to a desktop application. In an SPA, either all necessary code – HTML, JavaScript, and CSS – is retrieved with a single page load, or the appropriate resources are dynamically loaded and added to the page as necessary, usually in response to user actions. The page does not reload at any point in the process, nor does control transfer to another page. Interaction with the single page application often involves dynamic communication with the web server behind the scenes.

The SPA featured in this guide makes use of the following technologies: ASP.NET MVC 4, Web API, Knockout.js and MongoDB.

1. Setting Up the Database

We are using MongoDB database in our single-page application. MongoDB is a NoSQL database, storing data not in tables, but in JSON-style documents. Follow these steps to set the database correctly:

  1. Download MongoDB from http://www.mongodb.org/downloads;
  2. Extract the downloaded archive to C:\mongodb (or another location);
  3. In C:\mongodb\bin create a folder named data and in it - one called db. There should now be the following path: C:\mongodb\bin\data\db;
  4. To start MongoDB, open Command Prompt and type:


  5. To be able to use MongoDB in .NET, the Official MongoDB C# driver should be installed. To do so, in Visual Studio, go to ToolsLibrary Package ManagerPackage Manager Console and type:

    PM> Install-Package mongocsharpdriver

    in the console.

2. Project Overview

You can download our jQWidgets SPA with Knockout and MongoDB example project from here. In the subsequent steps, we will "reverse-engineer" the code and examine each of its functionalities in detail. Since this is an MVC application, we will look into its Model (Step 3), Controller (Steps 4 and 5) and View (Steps 6 to 10).

Before you run the example, make sure you have set up MongoDB first (Steps 1.1 to 1.4). You do not need to install the C# driver, because it has already been included in the project.

The example has two views, which can be accessed from a single page - Timesheets and About. Timesheets shows the entries in our MongoDB database in a jqxDataTable instance and allows the basic CRUD functionalities - creating, reading, updating, and deleting of records. About shows information about the project and SPAs in general. Here is what the two SPA views look like:

3. Timesheets Model

We populate our database with timesheets, which have four propeties - FirstName, LastName, Month and Year. The timesheets model, Timesheet.cs can be found in the project folder Models. Here is the model code, bar the data annotations and JSON.NET serialization attributes:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Linq;
using System.Web.Mvc;
using Newtonsoft.Json;
namespace MvcApplication.Models
public class Timesheet : Entity
public Timesheet()
Year = DateTime.Now.Year;
public string FirstName { get; set; }
public string LastName { get; set; }
public int Month { get; set; }
public int Year { get; set; }
public IEnumerable<SelectListItem> Months
var months = DateTimeFormatInfo.InvariantInfo.MonthNames.Select((monthName, index) => new SelectListItem
Value = (index + 1).ToString(),
Text = monthName
months.RemoveAt(12); // 13th item is empty
return months;

Note that the Timesheet class inherits Entity, whose mdel can be found in Models\Entity.cs.

4. Timesheets Controller

Here is the code of the timesheets controller, TimesheetsController.cs, sutiated in Controllers\Api. It supports the HTTP verbs GET, POST, PUT and DELETE via its methods.

using System;
using System.Configuration;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using MongoDB.Bson;
using MongoDB.Driver;
using MvcApplication.Models;
using MongoDB.Driver.Builders;
namespace MvcApplication.Controllers.Api
public class TimesheetsController : ApiController
private readonly MongoDatabase _mongoDb;
private readonly MongoCollection<Timesheet> _repository;
public TimesheetsController()
var connectionString = ConfigurationManager.AppSettings["MongoDBTimesheets"];
_mongoDb = MongoDatabase.Create(connectionString);
_repository = _mongoDb.GetCollection<Timesheet>(typeof(Timesheet).Name);
// GET /api/timesheets
public HttpResponseMessage Get()
var timesheets = _repository.FindAll().ToList();
var response = Request.CreateResponse(HttpStatusCode.OK, timesheets);
string uri = Url.Route(null, null);
response.Headers.Location = new Uri(Request.RequestUri, uri);
return response;
// GET /api/timesheets/4fd63a86f65e0a0e84e510de
public Timesheet Get(string id)
var query = Query.EQ("_id", new ObjectId(id));
return _repository.Find(query).Single();
// POST /api/timesheets
public HttpResponseMessage Post(Timesheet timesheet)
string uri = Url.Route(null, new { id = timesheet.Id }); // Where is the new timesheet?
var response = Request.CreateResponse(HttpStatusCode.Created, timesheet);
response.Headers.Location = new Uri(Request.RequestUri, uri);
return response;
// PUT /api/timesheets
public HttpResponseMessage Put(Timesheet timesheet)
var response = Request.CreateResponse(HttpStatusCode.OK, timesheet);
string uri = Url.Route(null, new { id = timesheet.Id }); // Where is the modified timesheet?
response.Headers.Location = new Uri(Request.RequestUri, uri);
return response;
// DELETE /api/timesheets/4fd63a86f65e0a0e84e510de
public HttpResponseMessage Delete(params string[] ids)
foreach (var id in ids)
_repository.Remove(Query.EQ("_id", new ObjectId(id)));
return Request.CreateResponse(HttpStatusCode.NoContent);

When an instance of TimesheetsController is created, it establishes a connection to MongoDB and creates a repository (MongoCollection) for the timesheets.

5. Home Controller and Populating the Database Initially

To demonstrate the Knockout binding of our SPA we need some initial data which to bind. For that purpose, add the following code to the Index method of HomeController.cs (in Controllers):

var mongoDb = MongoDatabase.Create(ConfigurationManager.AppSettings["MongoDBTimesheets"]);
var repository = mongoDb.GetCollection<Timesheet>(typeof(Timesheet).Name);
var timesheets = new List<Timesheet>
new Timesheet { FirstName = "Martha", LastName = "Jones", Month = 0, Year = 2014 },
new Timesheet { FirstName = "Rose", LastName = "Walker", Month = 5, Year = 2014 },
new Timesheet { FirstName = "Amy", LastName = "Williams", Month = 3, Year = 2014 },
new Timesheet { FirstName = "Lucie", LastName = "Queen", Month = 3, Year = 2014 }
foreach (var timesheet in timesheets)

After you run the application, the database should be populated with the four entries. Remove or comment out the database population code before the next run. Subsequent additions to the database will be done via the SPA's API (which will be examined in the next steps).

6. The Master Page

In Views\Shared you can find the file _master.cshtml, which loads all needed external resources (styles and scripts - jQuery, Knockout, jQWidgets, et al.) and renders the body of the page (through Index.cshtml, more on it - in Step 7). Here is the source code of _master.cshtml:

?<!DOCTYPE html>
<html lang="en">
<meta charset="utf-8">
<title>jQWidgets Single-page Application (SPA)</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="@Url.Content("~/css/bootstrap.css")" rel="stylesheet" type="text/css" />
<link href="@Url.Content("~/css/jqx.base.css")" rel="stylesheet" type="text/css" />
<link href="@Url.Content("~/css/jqx.orange.css")" rel="stylesheet" type="text/css" />
<link href="@Url.Content("~/css/site.css")" rel="stylesheet" type="text/css" />
<link href="@Url.Content("~/css/bootstrap-responsive.css")" rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/js/jquery-1.8.2.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/js/knockout-3.0.0.js")" type="text/javascript"></script>
<script src="@Url.Content("~/js/jqxcore.js")" type="text/javascript"></script>
<script src="@Url.Content("~/js/jqxdata.js")" type="text/javascript"></script>
<script src="@Url.Content("~/js/jqxdatatable.js")" type="text/javascript"></script>
<script src="@Url.Content("~/js/jqxscrollbar.js")" type="text/javascript"></script>
<script src="@Url.Content("~/js/jqxbuttons.js")" type="text/javascript"></script>
<script src="@Url.Content("~/js/jqxmenu.js")" type="text/javascript"></script>
<script src="@Url.Content("~/js/jqxwindow.js")" type="text/javascript"></script>
<script src="@Url.Content("~/js/jqxknockout.js")" type="text/javascript"></script>
<div id="pageTitle">jQWidgets SPA</div>
<script src="@Url.Content("~/js/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/js/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

7. Switching between Views

In this and the next steps, we will take a look at the functionalities, implemented in the file Index.cshtml in Views\Home.

To create an SPA effect, when you click on either Timesheets or About, the page is not reloaded. Instead, the views are just "switched" by hiding one of them and showing the other:

$('#jqxMenu').on('itemclick', function (event) {
var item = $(event.target).text();
if (item == "Timesheets" && $("#home").css("display") == "none")
$("#about").css("display", "none");
} else if (item == "About" && $("#about").css("display") == "none")
$("#home").css("display", "none");

8. Binding Timesheets to jqxDataTable

We will now focus solely on the Timesheets view as About contains only text.

First, in $(document).ready() we apply the Knockout bindings of our view model:


Then we need to load all timesheets from the database. This is done by calling the function loadTimesheets(), which is implemented in the view model:

loadTimesheets: function () {
var self = this;
'@Url.RouteUrl("DefaultApi", new { httproute = "", controller = "timesheets" })',
function (timesheets) {
$.each(timesheets, function (index, item) {
self.timesheets.push(new timesheet(item));

Once loaded, the timesheets are bound to the table with id timesheets. For each timesheet, a new table row is added and each row displays the data of its corresponding timesheet object:

<table id="timesheets">
<tbody data-bind="foreach: viewModel.timesheets">
<td data-bind="text: firstname"></td>
<td data-bind="text: lastname"></td>
<td data-bind="text: month"></td>
<td data-bind="text: year"></td>

And here is the definition of the timesheet object:

function timesheet(timesheet) {
this.id = ko.observable(timesheet.id);
this.firstname = ko.observable(timesheet.firstname);
this.lastname = ko.observable(timesheet.lastname);
this.month = ko.observable(timesheet.month);
this.year = ko.observable(timesheet.year);
this.update = function (timesheet) {

Finally, when the timesheets are loaded, initializeDataTable() is called, which transforms the plain HTML table into a styled jqxDataTable (see the first image above):

var initializeDataTable = function () {
var months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
theme: theme,
width: "800px",
altrows: true,
sortable: true,
selectionmode: "singleRow",
columns: [
{ text: 'First Name', dataField: 'FirstName', width: 250 },
{ text: 'Last Name', dataField: 'LastName', width: 250 },
text: 'Month', dataField: 'Month', width: 200, cellsRenderer: function (row, column, value, rowData) {
return months[parseInt(value) - 1];
{ text: 'Year', dataField: 'Year', width: 100, align: 'right', cellsAlign: 'right' }
$('#timesheets').on('rowSelect rowUnselect', function (event) {
var selectedRows = $("#timesheets").jqxDataTable('getSelection');

9. Create a New Timesheet or Update an Existing One

We will now look into the implementation of the "add timesheet" and "update timesheet" functionalities. By clicking the Add timesheet button, a modal window opens, containing a form for adding a new timesheet to the database:

If you select a row from the data table and click Update timesheet, the same window shows up, but this time the form is filled with the selected row data:

When we add/update information in the form and click Save, the postTimesheet() function is called:

postTimesheet: function (form) {
form = $(form);
if (!form.valid())
var newRowData = this._getTimesheetFromFrom(form);
var json = JSON.stringify(newRowData);
var update = form.find("input[type='hidden'][id='id']").val();
var httpVerb = !update ? "POST" : "PUT";
var self = this;
url: '@Url.RouteUrl("DefaultApi", new { httproute = "", controller = "timesheets" })',
type: httpVerb,
data: json,
dataType: 'json',
contentType: 'application/json; charset=utf-8',
success: function (jsonObject) {
if (update)
var match = ko.utils.arrayFirst(self.timesheets(), function (item) {
return jsonObject.id === item.id();
$("#timesheets").jqxDataTable('updateRow', viewModel.selectedRow().uid, { FirstName: jsonObject.firstname, LastName: jsonObject.lastname, Month: jsonObject.month, Year: jsonObject.year });
self.timesheets.push(new timesheet(jsonObject));
// adds a new row to the data table
var rowsCount = $("#timesheets").jqxDataTable("getRows");
$("#timesheets").jqxDataTable('addRow', rowsCount.length, newRowData);

It determines whether this is a create or update operation and selects the appropriate HTTP verb (POST or PUT). An Ajax call then updates the database accordingly. In the success callback function, the data table is updated to reflect the database. Then the window is closed.

10. Delete a Timesheet

In this step we will take a look at the "delete timesheet" functionality of the SPA. When you click on the Delete timesheet button, another modal window opens, asking the user if he really wants to delete the timesheet. If Delete is clicked, the function deleteTimesheets() is called:

deleteTimesheets: function () {
var index = getSelectedIndex(viewModel.selectedRow());
var ids = new Array(this.timesheets()[index].id());
var self = this;
url: '@Url.RouteUrl("DefaultApi", new { httproute = "", controller = "timesheets" })',
type: 'DELETE',
data: ko.toJSON(ids),
contentType: 'application/json; charset=utf-8',
success: function () {
$.each(ids, function (index, id) {
var match = ko.utils.arrayFirst(self.timesheets(), function (item) {
return id === item.id();
$("#timesheets").jqxDataTable('deleteRow', index);

It makes an Ajax call, which deletes the selected timesheet from the database. On success, the timesheet is removed from the data table as well.

11. Conclusion

The jQWidgets SPA with Knockout and MongoDB presents the user with all CRUD functionalities (create, read, update, delete) and exemplifies a single-page application, where the database is accessed and updated via Ajax calls and the page does not need to be reloaded to make changes visible to the user.