It seems you cannot turn in one direction or another and not hear about a new JavaScript library or CSS framework that promises to be the silver bullet - to be THE thing that will make web-based application development a breeze. This article will introduce you to Knockout.js (https://knockoutjs.com/). Knockout.js is an open source library (under the MIT License) that is pure JavaScript that works with any existing web framework and every mainstream browser. Further, Knockout.js is fully documented with a complete set of online tutorials. What does Knockout.js do? It simplifies the task of building data-aware web UIs through the application of the Model-View-View-Model (MVVM) pattern. Regardless of the business problem your web applications are built to solve, all web applications, and just about every other application for that matter, shares the following characteristics:

  • Create data
  • Retrieve data
  • Update data
  • Delete data
  • Display data

In addition to the CRUD operations, I added an extra D for display because users interact with the data through the user interface (UI). In the web application space, we can spend a significant amount of time and energy writing JavaScript code that drives the UI. If you face this issue, then you want to add the Knockout.js library to your toolbox.

Often, developers identify Knockout.js with Single Page Applications (SPAs). Indeed, Knockout.js makes it very simple to build SPAs. To learn about how Knockout.js helps you build SPAs, refer to the tutorials at knockoutjs.com. While the concepts I introduce in this article apply to SPAs, this article will focus on non-SPA scenarios.

MVVM Primer

The Model-View-View-Model (MVVM) pattern is an architectural design. Microsoft designed MVVM in conjunction with the data-binding features in the Windows Presentation Foundation (WPF). MVVM provides a clear separation of concerns between the user interface (UI) and the business logic. MVVM shares a lot in common with the Model View Controller (MVC) and Model View Presenter (MVP) patterns. And for that reason, I will take a few moments to discuss the MVC and MVP patterns.

In the MVC pattern, a view acts as the broker between the Model (the data displayed in the View) and the Controller (server-side endpoint) that takes data from the View and performs some action on that data and provides a response. In MVC, there is no data binding per se. Rather, through templates, as markup is processed on the server, data values are introduced so that what is presented to the browser is a combination of markup and data. ASP.NET MVC, Rails and the Backbone JavaScript library are frameworks that implement the MVC pattern.

The MVP pattern has a certain amount of logic in the presentation layer that is responsible for brokering actions between the Model and the View. In MVP, the presentation layer assumes the functions of the Controller in the MVC pattern. Where there is no data binding in MVC, there is data binding in MVP. In the .NET space, WPF, Windows Forms and ASP.NET Web Forms are examples of the MVP pattern. In the MVP pattern, data binding and UI refreshes are more automated than in the MVC pattern.

How does the MVVM pattern figure into the mix? The MVVM pattern is a hybrid of the MVC and MVP patterns, bringing the best of both together. MVVM has MVC's separation of concern benefits and MVP's data binding features. MVVM is primarily focused on the UI layer, leaving business logic to either be implemented on the server or in the presentation layer. To understand the MVVM pattern better, we must focus a moment on the View Model.

The View Model

The View Model, as the name implies, is the Model for View. The View Model is a superset of the Model (the M in MVC and MVP). Where the Model is a logical representation of a business entity such as an invoice or a customer, the View Model includes the Model plus other metadata the View may require. Examples of the metadata may include the background color, applicable CSS classes as well as the web page's height and width. What you choose to include in a View Model is a judgment call on your part, which should be driven by your application feature's business and technical requirements.

One More Pattern to Discuss - the Observer Pattern

I'll now discuss the Observer pattern to complete this basic primer on MVVM. It's the last one discussed here but it may be the most important one to discuss and understand when it comes to understanding how the MVVM pattern works. The core concept in MVVM is data binding which can be uni-directional and bi-directional. In uni-directional binding, the binding occurs one time and is normally used for read-only situations. Bi-directional binding, as the name implies, occurs in both directions. Like uni-directional binding, the target is hydrated with the source data value. Unlike uni-directional binding, when the target is updated, the source data is also updated to reflect the new value. This is the essence of the Observer pattern. Via events, a subject object notifies its dependents (the observers in this case) that a change in state has occurred. The observer is notified of the change and in that notification; the new data value is received as well.

A subject object would typically be something like a textbox. The observer would be the property in the model that is contained within the View Model to which the subject is bound. With that background, let us dive into how to implement Knockout.js in your web applications.

Data Binding

In this section, I'll discuss what the "Hello World"example is effectively for Knockout.js. Relative to the MVVM primer in the previous section, the following is an example of uni-directional binding where a DOM object is hydrated with a data value hosted within the View Model.

You should make sure you have downloaded Knockout.js from knockoutjs.com. In this article, I'll use version 2.2. The following code represents some simple HTML markup that references Knockout.js's data binding feature:

<body>
    <p data-bind="text: helloValue"></p>
    <p data-bind="text: worldValue"></p>
</body>

The previous code snippet shows two p tags each with the data-bind attribute and value assigned. The text contained within the first p tag will get its value from the helloValue property and the text within the second p tag will get its value from the worldValue property. The question is, where are these properties hosted? The answer is the View Model. From the previous discussion on the MVVM pattern, the View Model holds the data for the View. The following code snippet shows the View Model to support the Hello World data binding:

function helloWorldViewModel() {
        this.helloValue = "Hello";
        this.worldValue = "World";
    }

At this point, we have HTML markup that references data and we have a View Model that holds the data. In this case, the View Model and the Model are one and the same thing. In later examples, I will separate the two. For purposes of this example, the Model and View Models overlap; and there are times in practice where you may choose to follow this approach. So how do we connect the View to the Model/View Model? In other words, how do we connect the MVVM? The following code snippet fills that gap:

ko.applyBindings(new helloWorldViewModel());

The ko object is the global reference to Knockout. For the page we are on, Knockout traverses the DOM and searches for the data-bind attributes. From there, it is simply a matter of matching up the names. Put together, Listing 1 illustrates the code for the Hello World View.

Figure 1 illustrates how the page appears in Firefox browser. I used FireBug to illustrate the markup as it was rendered to the browser.

Figure 1:  Knockout.js applies data bindings after the view has been rendered to the browser.
Figure 1: Knockout.js applies data bindings after the view has been rendered to the browser.

Now that you understand the bare bones basics of implementing and data binding with Knockout.js, let's extend the View Model to elements that control how the display will appear.

Extending the View Model to Include UI Elements

Up to now, our View Model has only contained the data elements that would be hosted in the Model. A View Model, in the MVVM pattern, will often contain items that affect the View's appearance and behavior. This notion requires us to expand our traditional notions of what “data” is in the data binding context. In the MVVM pattern in Knockout.js, in addition to elements that describe business entities (the Model), the following partial list of items also qualify as data:

  • CSS styles
  • HTML attributes
  • Events: click, hover, etc.
  • Control Flow (if, foreach, etc.)

In this section, I will concentrate on Knockout.js's attribute binding feature to control the body tag's background color. You only have to make simple changes. To the View Model:

this.backGroundColor = "Blue";
<body data-bind="style: {backgroundColor: backGroundColor}">

All other code in Listing 1 remains the same. Figure 2 illustrates how the page now appears.

Listing 1: Hello World View using Knockout.js

<!DOCTYPE html>
<html lang="en" 
      xmlns="<a href="http://www.w3.org/1999/xhtml";>http://www.w3.org/1999/xhtml<;/a>">
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
    <p data-bind="text: helloValue"></p>
    <p data-bind="text: worldValue"></p>
</body>
</html>
<script src="js/<a href="http://knockout-2.2.0.js">knockout-2.2.0.js</a>"></script>
<script>
    function helloWorldViewModel() {
        this.helloValue = "Hello";
        this.worldValue = "World";
    }
    ko.applyBindings(new helloWorldViewModel());
 
</script>
Figure 2:  Knockout.js bindings can also control how the View appears.
Figure 2: Knockout.js bindings can also control how the View appears.

I want to call out the naming convention used in body tag's data binding. If the markup was static and we simply wanted to apply an inline style, the code would appear as follows:

<body style="background-color: blue">

Remember, Knockout.js is a JavaScript library. In JavaScript, hyphenated identifiers are not valid. Therefore, the following code would not work:

<body data-bind="style: {background-color: backGroundColor}">

To account for and manage this incompatibility, hyphenated identifiers that are valid in CSS are camel-cased such that the hyphen is removed and the first letter after the hyphen is upper-cased:

background-color = backgroundColor

A Quick Word about Design and Testing

We are not too far into this introduction and already you can see how View Models can quickly become an unruly mess! In this small example, where is the line between the Model and the View Model? What if I need to re-use the Model? As it stands now, reuse is not possible. How about testing? Can I unit test the validity of just the Model? As it stands now, no. I'll do a little refactoring to address these concerns. Listing 2 illustrates the refactored JavaScript code.

Listing 2: Embedded JavaScript refactored to helloworld.js

function helloWorldModel(helloValue, worldValue) {
    var self = this;
    self.helloValue = helloValue;
    self.worldValue = worldValue;
}
 
function helloWorldView(backGround) {
    var self = this;
    self.backGround = backGround;
}
 
function helloWorldViewModel(helloWorldModel, helloWorldView) {
    self = this;
    self.modelData = helloWorldModel;
    self.viewData = helloWorldView;
}
 
ko.applyBindings(
    new helloWorldViewModel(
       new helloWorldModel("Hello", "World"),
       new helloWorldView("blue")
    )
);

Listing 3 illustrates the modified markup. With the JavaScript moved to a separate file, the markup is much cleaner. Note the revised variable names in the data bindings.

Listing 3: Modified hello world HTML markup.

<!DOCTYPE html>
<html lang="en" 
      xmlns="<a href="http://www.w3.org/1999/xhtml";>http://www.w3.org/1999/xhtml<;/a>">
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body data-bind="style: {backgroundColor: viewData.backGround}">
        <p data-bind="text: modelData.helloValue"></p>
        <p data-bind="text: modelData.worldValue"></p>
    </body>
</html>
<script src="js/<a href="http://knockout-2.2.0.js">knockout-2.2.0.js</a>"></script>
<script src="js/helloworld.js"></script>

With this revised code, I've facilitated reuse and testing. With the code properly re-factored, let's turn our attention to bi-directional binding using observables.

Bi-Directional Binding with Observables

Uni-directional binding is useful. But if that is all we had, then we would have to manually manage the process of updating data models with user input. Aren't tools like Knockout.js designed to eliminate that busy work? Yes. To demonstrate bi-directional binding and observables, please note the following markup:

<p>
  <input data-bind="value: modelData.helloValue" />
</p>
<p>
   <input data-bind="value: modelData.worldValue" />
</p>
<p>
   <input data-bind="value: viewData.backGround" />
</p>

Figure 3 illustrates our hello world page.

Figure 3:  Hello world page with input text boxes bound to model data.
Figure 3: Hello world page with input text boxes bound to model data.

We don't have bi-directional binding yet. In other words, updating the data in the input text boxes won't update the text labels or the background color. To facilitate that, we need to add Knockout.js's observable feature to the applyBindings call:

ko.applyBindings(
    new helloWorldViewModel(
        new helloWorldModel(
            ko.observable("Hello"),
            ko.observable("World")
        ),
        new helloWorldView(
            ko.observable("blue")
        )
    )
);

Note, neither the helloWorldModel, helloWorldView nor the helloWorldViewModel is aware of the fact that the objects passed to their constructors are observable. What does that mean? In this case, when the values of these underlying properties change, anything bound to these data items change to reflect those new data values. Figure 4 illustrates this concept. The text labels at the top of the page reflect what was entered into the textboxes. The same is true for the CSS styling. The page's background color is set to the color specified in the third textbox.

Figure 4:  The hello world example with bi-directional binding with observables.
Figure 4: The hello world example with bi-directional binding with observables.

Conclusion: Where to Go from Here?

This article only scratches the surface of what you can accomplish with Knockout.js. In a real-world application, you may access your data via a web API that would serve data as JSON. JavaScript interprets this JSON as an object and could serve as our data model that in turn, would be used in a View Model. Perhaps part of the data returned could be used to alter the display. From here, you should consult the online Knockout.js tutorials at knockoutjs.com. The tutorials and documentation will guide you through every Knockout.js feature. Remember that Knockout.js is nothing more than pure JavaScript. That means if it's possible in JavaScript, it's possible in Knockout.js.

Knockout's chief benefit is that it automates the data binding for your web interfaces. Although the examples in this article are simple, they nevertheless demonstrate that no custom code is required to manage the data binding process. As you learn more about Knockout.js, data is much more than simple values demonstrated in this article. To demonstrate that, consider this additional View Model code:

self.clickHandler = function () {
    alert("You clicked me!");
};

What if you wanted to bind the handler to the page body? You may recall that the page body already has a data binding for the background color. The good news is that Knockout.js supports multiple bindings:

<body databind = "style: {backgroundColor: viewData.backGround},
   click: clickHandler">