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.



Related Articles

Downloads

Comments

  • Best tutorial I've read for MVVM pattern for asp.net!

    Posted by thewalkingjed on 02/13/2014 02:23am

    Thanks for this great article. I'm new to the mvvm design pattern and I've learned so much here. Looking forward to more great articles from you. Thanks!

    Reply
  • Nye GHD Limited Edition Lyserød Orchid

    Posted by motherdhmm on 05/30/2013 12:15pm

    [url=http://www.buy-beatsdrdre.com/category/monster-beats-australia]monster beats australia[/url] En fremragende “øjeblikkelig conditioner” er almindeligvis en bestemt dette specifikke præcise klæder din gode hår dage krøllet hår glatning klubber samt giver disse fysik foruden glimt. Speedy hår hårbalsam minimere den faktiske unikke forbedrede neglebånd, oprette dine distinkte god Ghd Tilbud Tresses gør dig hår jern moderat desuden opnås, med hinanden ved hjælp af assistance sikre den faktiske tydelig indre design i de ekstraordinære vilde hår tidsperioder basis via skade gennem betyder at involvere medvirken såsom en filtrering. Denne form for afhjælpning vilde hår emne vil rutinemæssigt blive gengivet anytime ens personlige Ghd hår styling jern vil blive skyllet ud. [url=http://www.buy-beatsdrdre.com/]beats by dre headphones[/url] Graviditet kan også forårsage hårtab, normalt tre måneder efter, at kvinden undfanget hendes barn. hårpleje er vigtigt at undgå disse hår betingelser. Ved at spise en velafbalanceret kost, er hår givet sin fulde sundhedsmæssige fordele. Vask håret forsigtigt med shampoo en gang om dagen, lathering blidt, og ikke gnide i håret for meget på håndklæde til at tørre viser sig at være effektive i at tage sig af ens kronen. Undgå brug af hårtørrer så meget som muligt, og stil hår, når det er tør eller fugtig, ikke når de stadig er våde. Vær omhyggelig med at bruge visse hårplejeprodukter, da der er kemikalier (især dem der findes i hår farve eller hår glatning behandlinger), kan der også beskadige hai [url=http://www.blog.cheapbeatsbydre.co.nz/monster-headphone]monster beats headphone[/url] hårpleje er vigtigt at undgå disse hår betingelser. Ved at spise en velafbalanceret kost, er hår givet sin fulde sundhedsmæssige fordele. Vask håret forsigtigt med shampoo en gang om dagen, lathering blidt, og ikke gnide i håret for meget på håndklæde til at tørre viser sig at være effektive i at tage sig af ens kronen. Undgå brug af hårtørrer så meget som muligt, og stil hår, når det er tør eller fugtig, ikke når de stadig er våde.

    Reply
  • CRUD using KnockoutJS and ASP.Net WebForm

    Posted by MasterPogi on 05/10/2013 01:50am

    Can you also include insert, update, delete thru WebMethod. Thanks

    Reply
  • Lightweight smart – Nike Loose TR Attack in spring 2013 3 series

    Posted by Tufffruntee on 04/21/2013 04:26pm

    Nike Emancipated TR Stalwart 3 noteworthy features is to purchase the trendy plot: Nike Self-ruling 5 soles improved bending Striation; supplemental tractor layout making training more focused when; lighter load, the permeability is stronger, and more in fashion shoe designs not not aim for shoes [url=http://fossilsdirect.co.uk/glossarey.cfm]nike huarache[/url] more serene wearing, barefoot training sensible of, but also more in fashion appearance. Nike Manumitted TR Then 3 provides first-class lateral perseverance, you can be suffering with the legs in the untenable during training. Strong vamp majuscule letters breathable grating, demean foam's one of a kind delineate can be [url=http://markwarren.org.uk/goodbuy.cfm]nike free run uk[/url] seen from stem to stern it. Lightweight, rugged, reduce foam material familiar through merest occasional seams, more obedient, advocate is stronger. Need more help, department of a training exercise, bubbles come in more parts of the need in return agreeableness, foam loose. Use two-ply say nothing moisture wicking mock materials, unshiny on your feet, refrain from living feet sear and comfortable. Phylite [url=http://markwarren.org.uk/goodbuy.cfm]nike free uk[/url] midsole offers lightweight stupor unceasing, special durability and stable outsole can do to greatly adjust the all-embracing weight of the shoe. Qianzhang pods on the outsole and heel-shaped Na媣e rubber enhances the shoe multi-directional traction on extraordinary surfaces.

    Reply
  • MVVM / Composite

    Posted by Dale on 01/15/2013 07:29pm

    Great article, knockout.js is awesome! I've created a composite framework that is a relatively complete MVVM framework. It's got similarities to Microsoft's Prism and is being used in a fairly large and complex product targeting web and mobile platforms. Check it out: http://danderson00.blogspot.com/2012/08/introducing-knockoutcomposite.html

    Reply
  • Welcome to Client Side Era

    Posted by Ahmad Dwedar on 11/01/2012 06:39am

    Very Helpful to start - thank you

    Reply
  • great article

    Posted by Chouteau on 07/11/2012 06:04am

    Perfect to begin

    • Attention, ther is a small Error !

      Posted by Sigi on 10/23/2012 07:20am

      I spent 2 Hours, therefore I want to let everybody know that there is an error in this line: var customers = $.map(results.d, function (item) { "results.d" should just be typed "results"

      Reply
    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • The first phase of API management was about realizing the business value of APIs. This next wave of API management enables the hyper-connected enterprise to drive and scale their businesses as API models become more complex and sophisticated. Today, real world product launches begin with an API program and strategy in mind. This API-first approach to development will only continue to increase, driven by an increasingly interconnected web of devices, organizations, and people. To support this rapid growth, …

  • Live Event Date: September 10, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild". This loop of continuous delivery and continuous feedback is …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds