Καλώς ορίσατε στο dotNETZone.gr - Σύνδεση | Εγγραφή | Βοήθεια

C# and .NET Tips and Tricks

Quests in programming in .NET
(Windows8) WinJS Basic Javascript Objects and the ViewModel pattern

When someone starts implementing small programs in Visual Studio 2011 and starts playing with the VS2011 templates for Metro applications in Javascript, he soon wonders about the differences that WinJS brings in Javascript object creation and what is the best practice of connecting the view (HTML) with the model (Javascript). he second relates to the functionality that WinJS brings and is similar to other data binding libraries like for example Knockoutjs.In this blog we will see a general overview of the object and ViewModel creation patterns using only WinJS via the implementation of a simple “Metro Style” application.

 

Please note that due to the lack of complete documentation for the WinJS library and its workings, some of the statements below are my assumptions and may not reflect 100% the reality. Of course every single line of code below is tested and works but bear in mind that there may be a better way of doing this and I will definitely keep researching for it.

 

How do I try the examples in this post?

If you do not have Windows8 – VS 2011 installed go ahead and download the Consumer Preview from here, then follow the steps given either here for running Windows8 on a VM or here (amazing and totally recommended) for running Windows8 on a virtual partition but directly on your “metal”. Then you may install VS 2011 Express which comes along with Expression Blend from the files downloaded from here.

 

How do I define classes with properties and fields in WinJS?

Properties are object variables that have setters and getters. Fields are object variables that do not have getters and setter. It is also preferable to define classes within namespaces to avoid naming collisions with other variables of other libraries. Let us define within the namespace “Progware”, a class named “DemoClass” with two public fields “string1”, “string2”, a public property “number” and a private field “_number”. We will be using WinJS.Namespace.define for the namespace definition and WinJS.Class.define for the class definition. To implement this class with its namespace, within a file named “DemoClass.js” we define a self executing function as follows:

 

(function () {
"use strict";

WinJS.Namespace.define("Progware", {
DemoClass: WinJS.Class.define(
function (string1, string2,number) {


this.string1 = string1;
this.string2 = string2;
this.number = number;
}
, {

string1: "",
string2: undefined,


number: {
set: function (value) {
if (isNaN(value) || value < 0)
throw new Error("Invalid number");
this._number = value;
},
get: function () {
return this._number;
}
},
incrementCool: function () {
this._privateIncrementCool();
},
getCoolValue: function () {
return Progware.DemoClass.staticPropertyNumber;
},


_privateIncrementCool:function(){
Progware.DemoClass.staticPropertyNumber++;
}

},


{
staticPropertyNumber: 1
}),
namespaceNumberVariable: 55
});
})();

WinJS.Namespace.define requires two parameters: The namespace name (Progware) and a list of the namespace’s classes, methods and/or variables. In this namespace a “namespaceNumberVariable” variable is declared (ie a global variable that can be referenced from anywhere in your code using the namepsace’s prefix – “Progware.namespaceNumberVariable”) and also a class is declared using the WinJS.Class.define method.

 

The WinJS.Class.define method requires 3 parameters: a function to act as the constructor of the class, a list of instance members of the class (those are the normal properties, fields, methods of each object of this class ) and a list of static members which are the equivalent of the properties, fields and methods declared as static in .NET and can be referenced from everywhere with the prefix of the namespace and the name of the class (in this example “staticPropertyNumber” is a static field and can be referenced from everywhere in the application using the “Progware.DemoClass.staticPropertyNumber” name).

 

The constructor of the class initializes the properties and fields. Note here that just the process of initializing a property on the constructor adds it to the list of properties of the class without having to declare it later. Those are the merits (and fallacies) of the loosely typed world! It is generally though good practice to declare them also in the appropriate place (second parameter) for “code consistency”.  Also note the number property and the way its setter and getter is defined. They both use a “private” field “_number” that is implicitly defined just by its use.  Of course there is no such thing as privates in Javascript but in our case just prefixing a field with the underscore “_” makes the field not enumerable and it also hides it from VS2011 intellisense (this is the IDE actually enforces a missing feature of the language which is also pretty handy). The same applies for the private method “_privateIncrementCool”. Also note how the classe’s methods manipulate the static field “staticPropertyNumber” and the naming convention they use to reference it.

 

In general, in the code above you have a full blown namespace with its global variables and its classes (with public, private properties, fields and methods). This I dare to say is the equivalent .cs file we would write for a class in C# apart from the namespace variables which cannot exist in .NET.

 

How do I create a ViewModel following the MVVM pattern in WinJS?

Now suppose you want to obey the MVVM principle in WinJS. That is you want to create a JavaScript class that implements all the view’s  functionality and data and bind that class to the view (that is the html page that displays the data and reacts to user events). The requirements for our simple example are as follows: We need to create a “DemoClass” object and display its contents. When a user click a button named “Add Prefix” we want to prefix the current value of the “string1” property of the class with the letter “A”. Here is a snapshot of the result:

 

image

 

Create a new Javascript/Blank Application project in VS 2011. You get a blank application with some primitive structure. In the “js” folder create a “DemoClass.js” file with the code mentioned above. This file defines your “DemoClass” class. Now go to your “default.html” page and write your DIVs for displaying the data and also implement a BUTTON so that your view reflects the image above.

 

Where do I put the JavaScript code to display the “DemoClass” object data to the view?

The first answer is everywhere you like! You can use the “default.html” itself if you like. It is pure plain JavaScript and the HTML world! Of course you need to make sure that the DOM is ready using well-known techniques. There is though a better suited space in Metro style application you can put your code where you are sure that your DOM will be ready. In the “default.js” file created for you you have the “app.onactivated” event that will be called when your app initializes. At that place the initial if checks to see whether your application is activated from a “terminated” state, that is it was not running before (more of this in a future post). Within that you can be sure that the DOM is ready and the “DemoClass" has been initialized. You first approach to display the data can be something like the following:

 

var class1 = new Progware.DemoClass("STRING1", "STRING2", 14);
document.querySelector(".string1Direct").innerText = class1.string1;
document.querySelector(".string2Direct").innerText = class1.string2;
document.getElementsByClassName("numberDirect")[0].innerText = class1.number;

 

That is, you create your “class1” object and then you either use WinJS’s querySelector (a structure that resembles the way you would query for DOM elements using JQuery) or the more traditional getElementsByClassName or getElementById to assign to the appropriate DOM elements the “class1”’s values.

 

All is good with that but you need to know that with this approach you make your code aware of the view (it has to know the id and class names of the HTML Page) which violates the separation we are trying to achieve with the MVVM pattern. The second approach described below solves this issue and brings you closer to the ViewModel pattern.

 

You initialize again the “class1” object but this time your view has the “data-win-bind” properties pointing the properties of the “class1” object:

 

<div id="boundDiv" style="margin-bottom:10px">
string1: <span data-win-bind="innerText: string1" style="color:green"></span><br/>
string2: <span data-win-bind="innerText: string2" style="color:green"></span><br />
number: <span data-win-bind="innerText: number" style="color:green"></span><br />
</div>

Now in the same place as before (the app.onactivated event) we initialize our object and then call WinJS.Binding.processAllfor the binding:

 

var class1 = new Progware.DemoClass("STRING1", "STRING2", 14);
var boundDiv = document.getElementById("boundDiv");
WinJS.Binding.processAll(boundDiv, class1);

 

Note that we specify the root element for our binding. All children of this element “see” the same data to which they bind (in our case class1). All is great apart from the  fact that if we change in code any of the values of string1,string2 or number, this change will not be reflected to the UI. To be able to achieve this we need o make a little change to the class1 we bind as follows:

 

var class1VM = WinJS.Binding.as(class1);
WinJS.Binding.processAll(boundDiv2Way, class1VM);

Thus we use WinJS.Binding.as to get a new “observable” class named “class1VM” and the new class is the one used for the binding. Note that this new class is just a wrapper. Changes to the properties of this class will affect the properties of “class1”. The opposite does not hold.

 

The only thing left to do is provide the functionality for the button. Again a way to go is to bind something to the click event of the button element as follows:

 

document.getElementById("prefixString1NoBinding").onclick = function () {
class1VM.string1 = "A" + class1VM.string1;
}
 
This will both change the value of “class1.string1” and reflect the change in the UI. But again to use the ViewModel pattern we need a way to bind the “click event” of the button to a method in the ViewModel. Actually why won’t we implement a general ViewModel class that will has this method but it will also have the “class1” for the data:
 
(function () {
"use strict";
WinJS.Namespace.define("Progware", {
ViewModel: WinJS.Class.define(
function () {
this.class1 = new Progware.DemoClass("", "", 0);
this.onAddPrefix = this.addPrefix.bind(this);
},
{
class1: undefined,

addPrefix: function (eventObject) {
this.class1.string1 = "A" + this.class1.string1;
}
}, {}
)
});
})();

The HTML now will look like this:
 

<div id="boundDivVM" style="margin-bottom:10px">
string1: <span data-win-bind="innerText: class1.string1" style="color:green"></span><br/>
string2: <span data-win-bind="innerText: class1.string2" style="color:green"></span><br />
number: <span data-win-bind="innerText: class1.number" style="color:green"></span><br />
    <button id="prefixString1BindingVM" data-win-bind="onclick:onAddPrefix" /><br/>

</div>

 
We can actually now initialize the ViewModel in app.onactivated handler and bind it to the UI:
 
var defaultVM=new Progware.ViewModel();
defaultVM.class1 = WinJS.Binding.as(class1);
var javascriptViewModelDiv = document.getElementById("boundDivVM");
WinJS.Binding.processAll(javascriptViewModelDiv, defaultVM);

But wait a second!
 
Why the data-win-bind (onclick) refers to the onAddPrefix method and not on the addPrefix one? And what the hell is this.addPrefix.bind(this)?
 
Well, those of you who are in Javascript for some time you will know that having the handler directly reference the addPrefix method will make the “this” keyword used in it to refer to the whole window and not the object itself. The this.<function>.bind(this) sets the correct reference for the “this” keyword  order to be used from within the function and this is exactly what we need to do here (quite easy and actually really helpful). So instead of binding to “addPrefix” we bind to the method returned by “this.addPrefinx.bind(this)”.
 
This was a first approach to the ViewModel pattern in WinJS. If you need the source code please feel free to write me a message.
Share
Posted: Κυριακή, 11 Μαρτίου 2012 4:03 μμ από το μέλος iwannis
Δημοσίευση στην κατηγορία: ,

Σχόλια:

WinJS: does MVVM | Q Sites έγραψε:

# Ιουλίου 5, 2013 5:01 πμ
Ποιά είναι η άποψή σας για την παραπάνω δημοσίευση;

(απαιτούμενο)

(απαιτούμενο)

(προαιρετικό)

(απαιτούμενο)
ÅéóÜãåôå ôïí êùäéêü:
CAPTCHA Image

Ενημέρωση για Σχόλια

Αν θα θέλατε να λαμβάνετε ένα e-mail όταν γίνονται ανανεώσεις στο περιεχόμενο αυτής της δημοσίευσης, παρακαλούμε γίνετε συνδρομητής εδώ

Παραμείνετε ενήμεροι στα τελευταία σχόλια με την χρήση του αγαπημένου σας RSS Aggregator και συνδρομή στη Τροφοδοσία RSS με σχόλια