MVVM  - Model View ViewModel

A further refinement of MVP with the addition of data binding.

The Presenter is named ViewModel gets the data from the Model and presents it to the View. Data binding is used to synchronize data changes between the View and the ViewModel. The ViewModel updates the Model as needed.



Overview


MVVM (Model View ViewModel) is a more recent addition to the family of MV patterns. It was first documented by Microsoft in 2005 where it evolved when they began building rich UI applications based on WPF (Windows Presentation Foundation). Several built-in WPF features, including data binding and its commanding architecture, make it highly suitable for MVVM.

In addition to WPF and Silverlight, the MVVM pattern also took hold in the Adobe Flex community. More recently, MVVM is making inroads into the JavaScript community with MVVM frameworks like Knockout, Kendo MVVM, and Knockback. We will demonstrate MVVM with Knockout in a few moments.

In MVVM, the Controller has been renamed ViewModel, meaning that it exposes the relevant data from the Model to the View. The ViewModel provides 'a View into the Model'.

How does MVVM differ from the others? MVVM continues along the same line as MVP in which there is a clear separation between the presentation (View) and the business logic/behavior (ViewModel). The difference is that MVVM uses data binding between the presentation and the business logic which automates the communication between the two. The diagram below highlights the role of data binding in MVVM:

Data binding is a technique that binds two data sources together and synchronizes them. Say you have a textbox with a name on your page (the View). In your ViewModel you have a data item that stores the value entered in the textbox. Now, if the user changes the name, the data binding mechanism automatically updates the value in the ViewModel. Similarly, when the data item in the ViewModel changes it is automatically carried over to the page via data binding.

This example is called 2-way binding because it works in both directions. You may also run into 1-way bindings where the change is unidirectional.

Data binding usually involves declarative data binding annotations on the page. We will see examples of this in our implementation example.

Another important point about MVVM is that Views and ViewModels are paired, that is, a ViewModel is custom made for a particular View. It is not reusable. Typically, ViewModels hold data that is highly tailored for the presentation requirements on the page. For example, you may have a formatted price field, such as a string with a "$145.22" value (ready to display), whereas in the Model, these are unformatted floating point values. So, each page (or partial page) has its own ViewModel.

The ViewModel holds the data while the user is working on a page and it gets continually updated on any changes. But only when it gets notified, usually by a save click, will it update the Model which in turn saves the data to a backend database.

MVVM is a very loosely coupled. Unlike the Presenter in the MVP pattern, the ViewModel knows nothing about View or its visual components because this is all handled through data binding.

Implementing MVVM in JavaScript
The necessary plumbing required for data binding and templating is large. This prevents us from building MVVM from scratch. Instead, we will use Knockout, an open-source framework that follows the MVVM paradigm. It will provide you with a good feel for MVVM.

The example is a shopping cart where users can 1) add new products, 2) change quantities, and 3) remove line items. The grand total is maintained during these actions. Here is a screenshot of the page (you can see it below in action):

Initially the cart will be empty. Clicking the 'Add Product' button will insert a blank row into the cart with dropdown from which a product can be selected. Once selected, the following controls get updated on the same row: the unit price, the quantity, and the subtotal. Also the Total Value on the bottom right will be updated.

Changing the quantity will immediately update the Subtotal and Total Value fields. Clicking on remove will delete the row and the Total Value will be updated as well. Finally, clicking on Checkout will display the data to be sent to the server (in a real app we would first proceed to a checkout/payment page).

Here's the HTML markup -- for simplicity styles and class attributes have been removed

<table>
    <thead>
        <tr>
            <th>Product</th>
            <th>Unit Price</th>
            <th>Quantity</th>
            <th>Subtotal</th>
            <th> </th>
        </tr>
    </thead>
    <tbody data-bind='foreach: items'>
        <tr>
            <td>
                <select data-bind='options: $root.products, optionsText: "name", 
                        optionsCaption: "Select...", value: product'> </select>
            </td>
            <td data-bind='with: product'>
                <span data-bind='text: toMoney(price)'> </span>
            </td>
            <td>
                <input data-bind='visible: product,value: quantity, 
                                  valueUpdate: "afterkeydown"' />
            </td>
            <td>
                <span data-bind='visible: product, text: toMoney(subtotal())' > </span>
            </td>
            <td>
                 <a href='#' data-bind='click: $root.removeItem'>Remove</a>
            </td>
        </tr>
    </tbody>
</table>

<p>
   Total value: <span data-bind='text: toMoney(grandTotal())'> </span>
</p>

<button data-bind='click: addItem'>Add product</button>
<button data-bind='click: submit'>Checkout</button>

It has a <table> that displays the shopping cart, a <span> with the grand total, and two <button>s, one to add a new product, and another to check out the order and complete the transaction. Let's look at this in more detail.

By the way, the purpose of the next few paragraphs is not to get into the details of the Knockout declarative data binding syntax, but rather to give you a flavor of building an app following the MVVM architecture.

The table has a regular header row. Its body contains several declarative data bindings. An important attribute in Knockout is data-bind. It has many options but almost always contains a reference to a property on the ViewModel which it will bind to. Note that we will associate (i.e. apply) the ViewModel with the View which we will see shortly when reviewing the JavaScript code.

The table's tbody element has a data-bind attribute with 'foreach: items'. This tells Knockout to iterate over an array on the ViewModel called items. The items are the line items in the shopping cart.

In the first <td> we have a dropdown (<select>) that data-binds to $root.products. This is a reference to the root object which is the ViewModel; its products property is an array with all products for sale. The user selected value is stored in a property called product which is on the current line item (not the ViewModel).

The next <td> displays the unit price for the selected product. The data-bind = 'with: product' sets the context, which is the selected product on the line item. It has a price property which is bound and formatted on the next line. So, any time a different product is selected, its price changes with it.

The following <td> is where the quantity can be selected. It only is visible when a product is selected. Its default value is 1. The user can change this and the binding immediately changes the value in the ViewModel following each key hit, i.e. "afterkeydown".

The next <td> is a subtotal for the line item. It is a computed value that is only visible when a product is selected. It is bound to a method on the line item object called subtotal(). Formatting is handled by a utility function called toMoney.

Finally, we have a <td> with a remove link that allows the user to delete the line item from the shopping cart. It is data bound to a click event which triggers the $root.removeItem method on the ViewModel which removes the current line item.

Below the table the grand total is displayed. It is data bound to grandtotal() which is a method on the ViewModel.

Finally, two buttons have their click events bound to addItem and submit respectively, which are methods on the ViewModel.

Next, we'll review the JavaScript code starting with the Model.

var Product = function (id, name, price) {
    this.id = id;
    this.name = name;
    this.price = price;
}

var Model = function Model() {
    this.products = [];
    this.products.push(new Product(1, "Paper", 4.95));
    this.products.push(new Product(2, "Scissors", 9.95));
    this.products.push(new Product(3, "Pencils", 4.98));
    this.products.push(new Product(4, "Pens", 19.50));
    this.products.push(new Product(5, "Eraser", 1.50));
    this.products.push(new Product(6, "Folders", 12.95));

    this.getProducts = function () {
        return this.products;
    };
}

There is nothing to indicate that this Model is used in the context of a MVVM project. It is a regular Model object, just like we used in MVC and MVP. It contains an array with all products that users can purchase. Each product has an id, a name, and a price. In most real-world situations this data comes straight from a database.

Before moving on to the ViewModel let's first review CartItem which represents a line item in the shopping cart.

var CartItem = function () {
    var self = this;

    this.product = ko.observable();        
    this.quantity = ko.observable(1);

    this.subtotal = ko.computed(function () {
        return self.product() ? 
               self.product().price * parseInt("0" + self.quantity(), 10) :
               0;
    });
};

It has all the properties you expect to see in a line item on a shopping cart: a product (this is an object itself which contains product name and unit price), a quantity, and a subtotal value for the line.

The product is a method, an observable method to be precise. The variable ko is an instance of the Knockout binding and dependency tracking engine. The observable method monitors changes in the product and notifies any subscribers that a change has occurred.

The quantity is also an observable method. Its default value is 1 which is passed as an argument.

The subtotal is a computed value. The callback function passed into ko.computed computes the subtotal for the line. It automatically re-executes when product or quantity changes occur.

We're now ready to review the ViewModel.

var ViewModel = function () {
    var self = this;

    this.products = new Model().getProducts();

    this.items = ko.observableArray([new CartItem()]); 

    this.grandTotal = ko.computed(function () {
        var total = 0;
        $.each(self.items(), function () { total += this.subtotal() })
        return total;
    });


    // Buttons actions 
    this.addItem = function () {
        self.items.push(new CartItem())
    };

    this.removeItem = function (item) {
        self.items.remove(item)
    };

    this.submit = function () {
        var data = $.map(self.items(), function (item) {
            return item.product() ? {
                productId: item.product().id,
                quantity: item.quantity()
            } : undefined
        });
        alert("Info sent to server: " + JSON.stringify(data));
    };
};

var toMoney = function (value) {
    return "$" + value.toFixed(2);
};

ko.applyBindings(new ViewModel());

The first thing we do in ViewModel is to ensure that the nested functions will have a correct context by assigning the this value in a variable called self which then gets added to the function's closure (alternatively, we could have used bind(this) on all the methods).

The ViewModel assigns the Model's data to variable named products which, as you may recall, is data bound to the dropdown in the shopping cart.

The items property is an observableArray of shopping cart line items. It works like observable but tracks changes to the array itself, such as removal and addition of new items. This is the array that the shopping cart iterates over in the data-bind = "foreach: items" attribute in tbody.

The grandTotal is a computed dependency value that adds up all subtotals for all line items.

The addItem and removeItem methods add and remove line items from the shopping cart respectively.

The submit method is not involved in data binding. It iterates over the line items and collects the relevant information that can be submitted back to the server in JSON format.

A helper function called toMoney converts values to a monetary format. And this is all there is to the ViewModel.

As a final step we need to associate our ViewModel with the View. The last line shows how easy this is: simply call ko.applyBindings with a new instance of the ViewModel. This will start the data binding and data dependency tracking which continues throughout the lifetime of the page. The shopping cart is now ready for use.

The shopping cart below is ready to run.


MVVM
Product Unit Price Quantity Subtotal
           Remove

Total value:    
   

What is nice about these apps is that they are highly performant.



  MVP