Implementing MVVM Pattern in Web Applications Using Knockout

Introduction

Data driven web sites rely heavily on JavaScript and JavaScript based libraries such as jQuery. The client side programming tends to become complex as the user interface becomes more and more rich. In such situations features such as data binding and dependency tracking are highly desirable. With open source libraries such as Knockout it is quite easy to fulfill these requirements in ASP.NET web sites. This article is intended to give you a quick understanding of what Knockout is and how to get up and running with it in your ASP.NET applications. 

The MVVM Pattern

Before you go into the details of using Knockout, you should have a basic understanding of the Model – View – View Model (MVVM)) pattern, as Knockout makes extensive use of it. MVVM is a user interface level design pattern that divides the whole responsibility of UI into three pieces, viz. Model, View and View Model. These pieces are described below:

  • Model : Model refers to your application data. In a real world ASP.NET web application, your data will typically be stored in a SQL Server database and your UI gets it from the server by making AJAX requests or some similar technique.
  • View Model : View Model refers to your data and UI level operations that you wish to perform on the data. The operations may include business validations or conditional checks or updating certain parts of the web page. You can think of View Model as a wrapper over your model data that adds UI level operations to it.
  • View : View is the user interface of your application. A View interacts with View Model to invoke some operations. A View is updated whenever data from the View Model changes. For a web application a view typically takes the form of an HTML document.

What is Knockout?

Knockout is an open source JavaScript library that helps you develop rich data driven web user interfaces. Knockout makes use of MVVM pattern. The official web site of Knockout defines the library like this :

“Knockout is a JavaScript library that helps you to create rich, responsive display and editor user interfaces with a clean underlying data model. Any time you have sections of UI that update dynamically (e.g., changing depending on the user’s actions or when an external data source changes), KO can help you implement it more simply and maintainably.”

Some of the key features of Knockout are as follows:

  • Knockout is a free and open source JavaScript library.
  • Knockout is based on MVVM pattern.
  • Knockout works with any web framework and doesn’t have any dependencies.
  • Knockout is a small & lightweight library.
  • Knockout works on all leading browsers including Internet Explorer, Firefox, Chrome and Opera

You can download and read the full documentation of Knockout here.

Example – Customer Entry Form

How Knockout is used in ASP.NET web applications can be best understood with an example. So, let’s build a simple example that demonstrates how Knockout simplifies your job of data driven UI management. In the following sections you will build a web form to add, modify and delete customers from the Customers table of the Northwind database.

Creating Entity Framework Data Model and Writing Web Methods

Begin by creating a new ASP.NET Web Forms based web site. Add a new Entity Framework Data Model to the web site and configure it to use the Customers table of the Northwind database. The following figure shows how the Customer class of EF data model looks:

Customer class of EF data model
Customer class of EF data model

You won’t be using all the properties of the Customer class in this example. The UI will display only CustomerID, CompanyName, ContactName and Country. Next, you need to write two Web Methods – one for fetching existing Customer records and the other for saving the modified records. These two Web Methods go inside the default Web Form and are shown below:

[WebMethod]
public static Customer[] GetCustomers()
{
    NorthwindEntities db=new NorthwindEntities();
    var data = from item in db.Customers
               orderby item.CustomerID
               select item;
    return data.ToArray();
}

[WebMethod]
public static string SaveCustomers(Customer[] data)
{
    //save customer data to database
    return "Data saved to database!";
}

The GetCustomers() Web Method selects records from the Customers table and returns them as an array of Customer objects. The SaveCustomers() Web Method accepts an array of Customer objects and saves them to the underlying database. Though the actual logic of inserting, deleting and updating records is not shown in the SaveCustomers() method, the same can be added based on your requirements. The SaveCustomers() method  returns a string indicating successful save operation.

Refer Knockout Library and Add Web Form Markup

Now open the default web form and switch to its markup view. You will need to add a <script> reference to Knockout library as well as jQuery in the head section. The following markup shows these <script> tags:

<script type="text/javascript" src="Scripts/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="Scripts/knockout-2.1.0.js"></script>

The Customer Web Form consists of two HTML tables as shown in the following figure:

The Customer Web Form

 The Customer Web Form

The top part of the web form allows you add a new customer. The bottom part displays existing and newly added customers. Clicking on the “Save To Database” button saves the data to the server. The existing customer records can be modified directly by changing the data. The Delete link allows you to delete a customer.

The web form markup without Knockout specific attributes is shown below. You will add Knockout data binding features to this web form in later sections.

<form>
    <h3>Add New Customer</h3>
    <table>
      <tr>
        <td>Customer ID :</td>
        <td><input type="text"/></td>
      </tr>
      <tr>
        <td>Company Name :</td>
        <td><input type="text" /></td>
      </tr>
      <tr>
        <td>Contact Name :</td>
        <td><input type="text" /></td>
      </tr>
      <tr>
        <td>Country :</td>
        <td>
          <select></select>
        </td>
      </tr>
      <tr>
        <td colspan="2">
          <button type="button">Add Customer</button>
          <button type="button">Save To Database</button>
        </td>
      </tr>
    </table>
    <table border="0">
        <tr>
          <th>Customer ID</th>
          <th>Company Name</th>
          <th>Contact Name</th>
          <th>Country</th>
          <th>Action</th>
        </tr>
        <tbody>
        <tr>
          <td><span /></td>
          <td><input type="text" /></td>
          <td><input type="text" /></td>
          <td><select></select></td>
          <td><a href="#">Delete</a></td>
        </tr>
        </tbody>
    </table>
</form>

Creating View Model classes

The next step is to create View Model classes. Note that the classes you develop in this section are client side classes and are independent of the EF data model classes you created earlier. First, you need to create Customer  class that mimics the required properties of the Customer data model class from the server. So, add the following code in a <script> block inside the head section of the web form.

function Customer(data) {
  this.CustomerID = ko.observable(data.CustomerID);
  this.CompanyName = ko.observable(data.CompanyName);
  this.ContactName = ko.observable(data.ContactName);
  this.Country = ko.observable(data.Country);
}

The Customer class consists of four properties, viz. CustomerID, CompanyName, ContactName and Country (these are the properties you wish to access). The constructor accepts an instance of Customer and sets various properties. Notice the use of ko.observable() in the code. The ko is the Knockout core object. The observable() is a special JavaScript object provided by Knockout that notifies subscribers about any changes in the property being observed. Observables allow you to develop automatically updating user interfaces. Once the Customer properties are made observables you need not worry about synchronizing between UI and the model. The data binding features of Knockout keep them in sync for you.

Now create CustomerViewModel class that represents the View Model of the application. The CustomerViewModel is shown below:

function CustomerViewModel() {
  var self = this;
  self.Countries = ko.observableArray(['USA', 'UK', 'India']);

  self.Customers = ko.observableArray([]);

  self.CustomerID = ko.observable();
  self.CompanyName = ko.observable();
  self.ContactName = ko.observable();
  self.Country = ko.observable();

  self.AddCustomer = function () {
    self.Customers.push(new Customer({
      CustomerID: self.CustomerID(),
      CompanyName: self.CompanyName(),
      ContactName: self.ContactName(),
      Country: self.Country()
    }));
    self.CustomerID("");
    self.CompanyName("");
    self.ContactName("");
    self.Country("");
  };

  self.RemoveCustomer = function (customer) {
    self.Customers.remove(customer)
  };

  self.SaveToDb = function () {
    $.ajax({
      type:"POST",
      url:"CustomerForm.aspx/SaveCustomers",
      data: ko.toJSON({ data: self.Customers }),
      contentType: "application/json",
      success: function (result) {
        alert(result.d);
      }
    });
  };

$.ajax({
  type: "POST",
  url: 'CustomerForm.aspx/GetCustomers',
  contentType: "application/json; charset=utf-8",
  dataType: "json",
  success: function (results) {
    var customers = $.map(results.d, function (item) {
      return new Customer(item)
    });
    self.Customers(customers);
  },
  error: function (err) {
    alert(err.status + " - " + err.statusText);
  }
})
}

Notice the above code carefully. First, it stores a reference to “this” in a variable (self). This is done because the JavaScript keyword “this” changes the object being referred based on the context in which it is used. To refer the View Model class anywhere in the code you store its reference in self variable.

The code then declares an observable array that stores a few country values. Here you are using some fixed counties but you can also fetch them using an AJAX call. An observable array is similar to observable object discussed earlier but observes all the elements of an array rather than a single member.

To hold the existing and new customers the Customers array is declared. The code also declares four properties, viz. CustomerID, CompanyName, ContactName and Country.

The CustomerViewModel class has three methods, viz. AddCustomer(), RemoveCustomer() and SaveToDb(). The AddCustomer() method is responsible for adding a new Customer to the Customers array. The AddCustomer() method simply grabs the values of the four properties and constructs a new Customer object. The newly created object is added to the Customers array using the push() method. The four properties are set to blank values so that the textboxes become blank for a new entry. The RemoveCustomer() method removes a customer from the Customers array using the remove() method.

The SaveToDb() method makes an AJAX request to the SaveCustomers() Web Method using $.ajax() of jQuery. Recollect that the SaveCustomers() Web Method accepts an array of Customer objects. The Customers array from the CustomerViewModel is converted into its JSON form using ko.toJSON() method. The success function simply displays the success message returned by the SaveCustomers() Web Method.

To load the existing customer data when the page loads for the first time you need to make an AJAX call to the GetCustomers() Web Method. The GetCustomers() method returns an array of Customer objects. These Customer objects need to be mapped to the JavaScript Customer class you created earlier. This mapping is accomplished with the help of  $.map() jQuery method. The first parameter of the $.map() method is the source Customer array that is to be mapped. The second parameter is a callback function that allows you to process each element of the source array. It is the responsibility of this callback function to map source object to the required JavaScript object. In our example you return a new instance of Customer from the callback function.

Data Binding the HTML Elements

Now that you are ready with the CustomerViewModel class, let’s add data bindings that will actually display customer data. Modify the HTML markup of the web form as shown below:

<form>
    <h3>Add New Customer</h3>
    <table>
      <tr>
        <td>Customer ID :</td>
        <td><input data-bind="value: CustomerID" /></td>
      </tr>
      <tr>
        <td>Company Name :</td>
        <td><input data-bind="value: CompanyName" /></td>
      </tr>
      <tr>
        <td>Contact Name :</td>
        <td><input data-bind="value: ContactName" /></td>
      </tr>
      <tr>
        <td>Country :</td>
        <td>
          <select data-bind="options: Countries, value: Country,optionsCaption: 'Select Country...'"></select>
        </td>
      </tr>
      <tr>
        <td colspan="2">
          <button type="button" data-bind="click:AddCustomer">Add Customer</button>
          <button type="button" data-bind="click:SaveToDb">Save To Database</button>
        </td>
      </tr>

    </table>
    <table data-bind="visible: Customers().length > 0" border="0">
        <tr>
          <th>Customer ID</th>
          <th>Company Name</th>
          <th>Contact Name</th>
          <th>Country</th>
          <th>Action</th>
        </tr>
        <tbody  data-bind="foreach: Customers">
        <tr>
          <td><span data-bind="text: CustomerID" /></td>
          <td><input data-bind="value: CompanyName" /></td>
          <td><input data-bind="value: ContactName" /></td>
          <td><select data-bind="options: $root.Countries, value: Country"></select></td>
          <td><a href="#" data-bind="click: $root.RemoveCustomer">Delete</a></td>
        </tr>
        </tbody>
    </table>
</form>

Notice the markup shown in bold letters. Knockout uses the data-bind attribute to specify data bindings between underlying data source and HTML elements. There are many bindings that you can use. However, more commonly used bindings include:

  • text
  • value
  • visible
  • enable
  • options
  • click
  • submit

For one way data binding (i.e. read only display) you will typically use text binding and for two-way data binding (i.e. editable display) you will use value binding. Notice that in the above markup all the <input> textbox fields use value binding whereas the <span> element displaying CustomerID uses a text binding (since CustomerID is a primary key).

The bottom table uses data binding at two places. In the <table> tag and in the <tbody> tag. The <table> element uses visible binding such that the table is visible only if there are any elements in the Customers array (length > 0). The <tbody> element is bound with the Customers array using for-each binding. As you might have guessed the for-each binding essentially iterates through the Customers array and automatically generates a table row for every Customer. 

To select Country value you need to display a dropdown list (<select> element). A dropdown list can be data bound using a combination of options and value binding. The options binding specifies the source of data for the dropdown list. In our case countries come from the Countries array. The dropdown list from the top table uses the optionsCaption parameter to indicate the initial value to be displayed in the element. The dropdown list from the bottom table by default selects the current country value in the dropdown. Notice the use of $root while binding the second dropdown. Since the Countries array resides inside the CustomerViewModel class and not inside the Customer class, $root is used to indicate that the Countries array is to be picked from ViewModel class. The value binding indicates the property of the Customer class that is to be updated with the selection.

Various buttons (Add Customer, Save To Database and Delete) of the form make use of click binding to invoke the appropriate method of the CustomerViewModel. For example, clicking on the “Add Customer” button invokes the AddCustomer() method.

Activating the Data Bindings

The final step is to activate the data bindings when the page loads in the browser. The following code does that for you :

$(document).ready(function () {
  ko.applyBindings(new CustomerViewModel());
});

The ko.applyBinding() accepts a ViewMode class and activates the data bindings for the web page.

Summary

Knockout allows you to develop data driven rich user interfaces easily. It makes use of the MVVM design pattern and allows data binding in a variety of ways. It also automatically tracks changes to the data, helping you to synchronize the View and the View Model. This article gave an overview of this powerful and flexible library. With this knowledge you are ready to explore many more features that Knockout has to offer.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read