The goal of the Presentation Model pattern is to “Represent the state and behavior of the presentation independently of the GUI controls used in the interface” (from Martin Fowler’s website.) All of the data and behavior of the UI as it relates to the state of the model is contained in the model. The view (or the UI) simply renders the data and refers to the model to determine such things as control state and values. Non model related items (such as rendering and other solely UI constructs) are in the This minimizes interaction between the model and the UI, creating a clearer separation of concerns between the two layers.
The Model-View-ViewModel (M-V-VM) pattern was originally created by Microsoft to extend the Presentation Model pattern to take advantage of the features in WPF and SilverLight (SL). By using the advanced data binding capabilities of those frameworks as well as the command pattern implementation (especially in WPF), developers could easily create of a clear delineation between the model and the User Interface.
The pattern caught on like wild fire. You can’t shake a stick without hitting someone’s MVVM open source implementation, and it’s expanded well beyond WPF and SL. There are MVVM frameworks for Windows Phone as well as JavaScript implementations!
Why Not Just Use an Existing Framework?
There’s nothing wrong with using one of the many MVVM frameworks already in existence. However, before diving into using a framework, it’s important to understand the pattern and how it is implemented in your language of choice.
This series of blog posts is not meant to replace any MVVM frameworks, and doesn’t recommend one over the other. Choosing frameworks requires applying critical thought to your particular situation, and then deciding the best course. I won’t presume to be able to do that for you, not knowing anything about your unique circumstances.
Getting started
As mentioned above, the MVVM pattern has a deep reliance on built in data binding capabilities. In order to actively reflect model changes without having to write a bunch of plumbing to connect the model to the UI controls, two items must be in place – the models must be observable, and the data binding must be able to leverage the observable pattern.
Fortunately, the data binding capabilities built into Windows 8 WinJS is fully capable to take advantage of models that make themselves observable. This post explains how to use the WinJS.Binding namespace to create models that notify bound objects that an update has occurred (similar to the INotifyPropertyChanged and INotifyCollectionChanged interfaces in WPF and Silverlight).
Create the Project
For a more robust application, we would probably want to look at the Grid App or the Navigation App templates, but since I am only creating a simple ListView and Detail section, the Blank App template is perfect for our needs.
Select JavaScript/Windows Store in the left rail of the New Project Dialog, click on Blank App, then enter an appropriate Name and Location. Figure 1 details this process.
Figure 1 – Creating a new WinJS Blank Application project
Build the HTML
Next we will build the HTML to display the model data and verify that changes to the model do indeed update the bound elements.
Open up the default.html file and add the following markup (shown in Listing 1):
<div id="contactDetail" class="win-type-xx-large twoColumnLayout">
<div class="leftColumn" >
First Name: <br />
Last Name: <br />
Age: <br />
</div>
<div class="rightColumn">
<span data-win-bind="textContent:firstName"></span> <br />
<span data-win-bind="textContent:lastName"></span> <br />
<span data-win-bind="textContent:age"></span> <br />
</div>
</div> <br /><br />
<button id="updateContactCommand" class="win-type-x-large">
Update Contact
</button>
Listing 1 – default.html
There are two main components to our sample markup.
Contact Detail
The first section of the markup presents the details for a single contact. The main <div> uses the –ms-grid display option to format the labels on the left and the values on the right. I’ve added classes to the default.css file to help with formatting (shown in Listing 2). I’ve also used some of the classes defined in the WinJS supplied ui-dark.css file that uses recommended font markup – “win-type-xx-large” and “win-type-x-large”. It is recommended that you use the provided styles as much as possible to keep within the standard design guidance provided by Microsoft.
.twoColumnLayout
{
display: -ms-grid;
-ms-grid-columns: auto auto;
-ms-grid-rows: auto;
-ms-grid-row-align: start;
}
.leftColumn
{
-ms-grid-column: 1;
-ms-grid-row: 1;
-ms-grid-row-align: end;
text-align: right;
padding-right: 10px;
}
.rightColumn
{
-ms-grid-column: 2;
-ms-grid-row: 1;
padding-left: 10px;
}
Listing 2 – default.css (default values omitted for brevity)
Commands
There is a single command that we will use to demonstrate the observable nature of our model. It is used to update the contact detail programmatically, and if all works as planned, we will immediately see the changes reflected in the application.
NOTE: Normally, buttons would be added to the AppBar, and not placed in the middle of the user interface. To keep the example simple, I have placed the button right below the contact detail.
Application User Interface
The display will look like Figure 2 when the program is executing.
Figure 2 – Running Application
Build the Model
For this example, our Model will be very simple. The model will represent a contact where each contact consists of a first name, last name, and age.
Create a new file in the “js” directory named “viewModel.js”.
Side Bar: Standard Items in WinJS Code Files
I have adopted the habit of adding three key items to every WinJS JavaScript file right from the start.
- References to Microsoft WinJS core files
- Self executing function
- WinJS Namespace definition
While not required, the first one is to help with Visual Studio Intellisense (please see this post for more information). The next two insulate our code from some of the more “interesting” aspects of JavaScript, mostly dealing with the global namespace issues and the many faces of “this”.
To make adding these items easy, I’ve created a series of JustCode JavaScript templates that does all of this by simply typing one word! For more information on this, please see this post on my WinJS JustCode templates. Whether you use a code template or type the code in manually, the resulting code is shown in Listing 3.
/// <reference path="//Microsoft.WinJS.1.0/js/ui.js" />
/// <reference path="//Microsoft.WinJS.1.0/js/base.js" />
(function () {
"use strict";
WinJS.Namespace.define("ViewModel", {
})();
Listing 3 – Initial “standard” code for JavaScript file
Add viewModel.js to the default.html page
The next step is to add your viewModel.js file into the default.html page. You can either type in the script reference or drag the file from the solution explorer to the correct location in your html file as shown in Listing 4.
<!-- CodeMash references -->
<link href="/css/default.css" rel="stylesheet" />
<script src="js/viewModel.js"></script>
<script src="/js/default.js"></script>
Listing 4 – Adding viewModel.js to default.html
Creating a Standard Model
Our first attempt will be to create the model as part of the namespace itself. This involves updating the namespace from “ViewModel” to “ViewModel.Contact” and adding the required properties.
The last thing we do inside the self executing function (but outside of the namespace) is assign values to the Contact properties. The updated namespace and related code is shown in Listing 5.
/// <reference path="//Microsoft.WinJS.1.0/js/ui.js" />
/// <reference path="//Microsoft.WinJS.1.0/js/base.js" />
(function () {
"use strict";
WinJS.Namespace.define("ViewModel.Contact", {
firstName: '',
lastName: '',
age:0
});
ViewModel.Contact.firstName = "Philip";
ViewModel.Contact.lastName = "Japikse";
ViewModel.Contact.age = 44;
})();
Listing 5 – Updated viewModel.cs
Binding the Model to the HTML
Next we will bind the model to our markup in default.js. I prefer to create a function to encapsulate all of my initial setup code and call that function from the onActivated event (instead of placing all of that code in the event itself). This enables reuse and keeps my code easier to read and maintain.
The binding is very straightforward and takes place in the performInitialSetup function, as shown in Listing 6.
app.onactivated = function (args) {
if (args.detail.kind === activation.ActivationKind.launch) {
if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
performInitialSetup(args);
}
else {
}
args.setPromise(WinJS.UI.processAll());
}
};
function performInitialSetup(args) {
WinJS.Binding.processAll(
document.getElementById('contactDetail'),
ViewModel.Contact);
};
Listing 6 – Binding ViewModel to Markup in default.js
When we run the application, we see the First and Last Name as well as the Age displayed correctly. The button doesn’t do anything yet, but that’s because we haven’t wired it up yet!
Wiring Up the Command
We need to update the model outside of the user interface to prove that the bound items will update automatically. For this example, we just want to update the properties of the model in the click event of the button. We also use a MessageDialog to display the values of the Contact after the update is executed. The code is shown in Listing 7.
var updateContactButton = document.getElementById('updateContactCommand');
updateContactButton.addEventListener('click', function (e) {
ViewModel.Contact.firstName += "s";
ViewModel.Contact.lastName += "t";
ViewModel.Contact.age += 1;
var msg = new Windows.UI.Popups
.MessageDialog("The value of the contact is this:\r\n"
+ "First Name: " + ViewModel.Contact.firstName + "\r\n"
+ "Last Name: " + ViewModel.Contact.lastName + "\r\n"
+ "Age: " + ViewModel.Contact.age);
msg.showAsync();
});
Listing 7 – The click event handler
Running the Application
When we run the application and click on the button, we see the result in Figure 3. As you can tell, the model has been updated but isn’t showing in the application.
Figure 3 – Non-observable data model result
Building the Model – Take 2
We can either write a bunch of code to wire up the properties and the bindings, or we can use one of the really cool features in WinJS – the WinJS.Binding.as() function. To use this, we move the “Contact” part of the namespace to below the namespace declaration (this makes is a property of the namespace) with the value of WinJS.Binding.as({}). The properties that were in the namespace level are moved down a level (under the WinJS.Binding.as()). The updated code for the viewModel is shown in Listing 8.
WinJS.Namespace.define("ViewModel", {
Contact: WinJS.Binding.as({
firstName: '',
lastName: '',
age: 0
}),
});
Listing 8 – Updated ViewModel namespace code
Fortunately, this change require a change in any other code since the path is still ViewModel.Contact. Simply run the code again, click on the button, and you will see the results shown in Figure 4.
Summary
In this post we took a standard model and made it observable by using the WinJS.Binding.as() to add in the WinJS equivalent of the XAML INotifyPropertyChanged interface. In my next post in this series, we will add an Observable collection to the model.
You can download the code here.
Happy Coding!
About the author
Philip Japikse
|
Philip Japikse an international speaker, a Microsoft MVP, INETA Community Champion, MCSD, CSM/ CSP, and a passionate member of the developer community, Phil Japikse has been working with .Net since the first betas, developing software for over 20 years, and heavily involved in the agile community since 2005. Phil works as a Developer Evangelist for Telerik's RadControls for Windows 8 as well as the Just family of products (JustCode, JustMock, JustTrace, and JustDecompile) and hosts the Zero To Agile podcast (www.telerik.com/zerotoagile). Phil is also the Lead Director for the Cincinnati .Net User’s Group (http://www.cinnug.org). You can follow Phil on twitter via www.twitter.com/skimedic and read his personal blog at www.skimedic.com/blog. |