Building a RIA with HTML5, CSS3, JS and KnockoutJS Part 3: MVVM and Databinding

Are you migrating from Silverlight to HTML5? After reading this article, you will know how to re-use some silverlight techniques on the web

This Article is part of a three-part series:

Part 3: MVVM and Databinding with KnockoutJS

What is MVVM?

Here’s a quick MVVM summary from WIKI:

MVVM facilitates a clear separation of the development of the graphical user interface (either as markup language or GUI code) from the development of the business logic or back end logic known as the model (also known as the data model to distinguish it from the view model). The view model of MVVM is a value converter meaning that the view model is responsible for exposing the data objects from the model in such a way that those objects are easily managed and consumed

In this part we will use KnockoutJS to support MVVM and Databinding. Open the code you created in the last part or open this zip-file.

The project template from part one already contained knockout-2.1.0.js. I cannot express enough how impressed I am with this library and I encourage you to inspect the samples on the knockout website.

CustomerViewModel.js:

var CustomerViewModel = function() {
  var self = this;
  this.PageName = ko.observable("Customers");
  this.Customers = ["Dell","Microsoft","Apple"];
};

InvoiceViewModel.js:

var InvoiceViewModel = function () {
  var self = this;
  this.PageName = ko.observable("Invoices");
  this.Invoices = ["$15000", "$56", "$6544", "$56", "$6544"];
};

You can see that both viewmodels expose a PageName and a list of items. A real-world viewmodel could load these items asynchronously from a server but for the sake of simplicity we add some static demo customers and invoices.

<script type="text/javascript" src="ViewModels/CustomerViewModel.js"></script>
<script type="text/javascript" src="ViewModels/InvoiceViewModel.js"></script>
app = {};

app.initialize = function () {
    app.customerViewModel = new CustomerViewModel();
    app.invoicesViewModel = new InvoiceViewModel();
}
<script type="text/javascript">
app.initialize();
app.navigation = new Navigation("customers");
</script>

Bind the viewmodels to the HTML

<div id="root">
<h2 data-bind="text: PageName"></h2>
<ul data-bind="foreach: Customers">
<li data-bind="text: $data"></li>
</ul>
</div>
<script type="text/javascript">
ko.applyBindings(app.customerViewModel, document.getElementById("root"));
</script>

You can see that Knockout uses the data-attribute to bind the data from the viewmodel to our UI elements. To apply the bindings we use the ko.applyBinding method. This is the result:

the office

Hmmmm….no page title and no list of customers. The script block is not executed because we load the HTML dynamically. This little inconvenience led me to a very nice solution that mimics code behind files.

Code behind file

The idea of a code behind file is that presentation and behaviour are defined in separate files. One contains markup and the other contains code. In the HTML world you often see files that contain HTML and script in one file. The code above is a sample of that. So I wondered…How would Silverlight manage this? It has to parse XAML and when the document is loaded, it starts some Initialize function from a file that has belongs to the XAML file. They share the same name with a different extension. Like MainPage.xaml and MainPage.xaml.cs.

We can do that too! :-)

app.customers = {
    root:document.getElementById("root")
};
ko.applyBindings(app.customerViewModel, app.customers.root);

Now we need a way to load and execute this script. We use the jQuery getScript() method for that. Open navigation.js and load the script file when the HTML has been loaded. Here is the complete loadView method for your convenience:

this.loadView = function (view, pushHistory) {
    var contentframe = $("#content");
    contentframe.load("Views/" + view + ".html", function () {

        self.currentView = view;

        if (pushHistory) {
            window.history.pushState(view, null, "#" + view);
        }

        $.getScript("Views/" + view + ".js", function () {
        });
    });
}

The getScript method loads AND executes the script! Isn’t that nice:

customers

This way, the view and it’s code behind are the only ones who “know” who to bind to. The navigate class has no strong ties to models, viewmodels or views. That’s a good thing!

Now you can do the same thing for the Invoices view. Or you can download the finished project for this part.