November 2011 - Posts
KnockoutJS–The Observables
On my previous post, I’ve demonstrated a simple example
of data binding between our HTML content (view) and our Javascript (view-model). On this post I’ll dive, not deeply, to the javascript on our view-model and explain some of it’s basics, the ones that causes it to act as a real view-model.
ko
KO stands for Knockout and it will be used whenever we use the KnockoutJs library
ko.observable()
This function, of the KnockoutJs library notifies the view of every change of the bounded member’s value. Please note that in Javascript you don’t have actual properties (unlike C#) and you cannot use simple assignment syntax to invoke get method. Therefore, whenever you change member you use <member name>(<new value>). The library upon invocation will reflect the change on the browser’s screen.
Binding in KnockoutJs is two-way. (There is no binding-mode like XAML), meaning that the user changes the values whenever the member is bounded to an editable element (textbox, checkbox, combo-box etc.).
The observable method can be invoked with no parameters or with a value of which the member is initialized with.
For collections we have the observableArray who can have an empty array as parameter ([]) or with some items (syntax: ko.observableArray('[{item1, item2, …]}) );
More about collections when I talk about templates.
ko.applyBindings
That’s the command that set’s the HTML page’s data-context (XAML term once again). It get’s our view-model as a parameter.
Summary:
In this post I’ve reviewed some of the basic code of the view-model javascript. For every member we wish to bind we have to declare it as ko.observable or as ko.observableCollection for collections. At the end we should invoke the ko.applyBindings function which actually binds the data to the HTML element, or set the HTML page’s data context if you prefer XAML terminology.
Note: The ko.applyDataBindings() should come after the HTML content, otherwise it won’t know where to bind the data to.
KnockoutJs – MVVM in HTML & Javascript
Previously I’ve posted about MVVM and Silverlight, It seems long ago, before Microsoft has begun focusing on HTML & Javascript as a client development platform.
One of the things I’ve cherished the most about XAML related technologies development was the ability to completely decouple behavior and UI using MVVM. I didn’t know about any possibility to implement the same pattern on HTML & Javascript based applications.
In order to do so, we can now use KnockoutJS, a Javascript library based on jQuery that allows as to write an MVVM application with HTML and Javascript.
The first thing we’ll look for in MVVM is how to bind data from the View-Model. Our view-Model in this case will be a Javascript section separated from our HTML and looks like it doesn’t know anything about it (as MVVM should be). The view-model can certainly be on a separate file, however we’ll lack the intellisences doing so.
So, How we do it?
1. Obtain jQuery, you can do it by download it or linking to it directly. Note: You cannot use the version supplied by Visual studio 2010 because it’s minimum version is 1.4.2 (VS supplies 1.4.1)
2. Download KnockoutJs, latest javascript file. You can find the latest version on this download page.
3. Link your HTML file to both jQuery and KnockoutJs files
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script type="text/javascript" src="Scripts/jquery-1.6.2.min.js"></script>
<script type="text/javascript" src="Scripts/knockout-latest.js"></script>
<title></title>
</head>
<body>
<!-- Your Page layout HTML (view) here -->
<script>
//Your View-Model code (javascript) here
</script>
</body>
</html>
Note: The view-model code has to be after your HTML content.
After having our page set-up, let’s have some javascript at the view model.
on our HTML content let’s make a span, and bind it’s text content to a property in the view-model:
<span>The time now is : </span>
<span data-bind="text: today"></span>
Let’s look at our view-model code. There we’ll create a view-model, add a data member called “today” (which we’ve bind our span text to) and with a simple javascript code have it updated every second
var viewModel =
{
today: ko.observable(),
updateDate: function () {
viewModel.today(new Date());
}
}
}
//Init data
viewModel.today(new Date());
// call updateDate every second
setInterval(viewModel.updateDate, 1000);
//Sets the page's data context
ko.applyBindings(viewModel);
Now, when browsing our page, we’ll see the clock at the span.
Summary:
This is, of course, a very simple example of how we can bind, but imagine the possibilities now of having an entire application with javascript and HTML, contacting the server through AJAX and not through for submission. You can now separate completely your javascript code from your UI design just as you did in WPF/Silverlight working with XAML & C#.
Of course, there’s still need for templates, handle events such as button click or text changes but this is for later posts.
A problem I’ve encountered at a customer I work for was very strange behavior regarding
the WSDL being generated by it’s service. The WSDL didn’t contain any class property, moreover it did contain private members of a class being exposed by the service.
Let’s have a look on a repro:
1. Here’s the class being exposed:
namespace SerializationDemo.Entities
{
[Serializable]
public class SerializableEntity
{
public int SomeProperty { get; set; }
}
}
2. Here’s the class schema representation as being exposed in WSDL
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://schemas.datacontract.org/2004/07/SerializationDemo.Entities" elementFormDefault="qualified" targetNamespace="http://schemas.datacontract.org/2004/07/SerializationDemo.Entities">
<xs:complexType name="SerializableEntity">
<xs:sequence>
<xs:element name="_x003C_SomeProperty_x003E_k__BackingField" type="xs:int"/>
</xs:sequence>
</xs:complexType>
<xs:element name="SerializableEntity" nillable="true" type="tns:SerializableEntity"/>
</xs:schema>
3. Here’s the class as generated on client side
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="SerializableEntity", Namespace="http://schemas.datacontract.org/2004/07/SerializationDemo.Entities")]
[System.SerializableAttribute()]
public partial class SerializableEntity : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {
[System.NonSerializedAttribute()]
private System.Runtime.Serialization.ExtensionDataObject extensionDataField;
private int SomePropertyk__BackingFieldField;
[global::System.ComponentModel.BrowsableAttribute(false)]
public System.Runtime.Serialization.ExtensionDataObject ExtensionData {
get {
return this.extensionDataField;
}
set {
this.extensionDataField = value;
}
}
[System.Runtime.Serialization.DataMemberAttribute(Name="<SomeProperty>k__BackingField", IsRequired=true)]
public int SomePropertyk__BackingField {
get {
return this.SomePropertyk__BackingFieldField;
}
set {
if ((this.SomePropertyk__BackingFieldField.Equals(value) != true)) {
this.SomePropertyk__BackingFieldField = value;
this.RaisePropertyChanged("SomePropertyk__BackingField");
}
}
}
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName) {
System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if ((propertyChanged != null)) {
propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
}
As seen from the WSDL and from the auto-generated client code, instead of getting the property SomeProperty of SerializableEntity you get the class's private field, which was the compiler-generated backing field (_x003C_SomeProperty_x003E_k__BackingField) of the automatic property.
Why did it happened?
SerializableAttribute indicates that all fields of the class marked Serializable can be serialized, including of course the private ones. However, it doesn’t say anything about the properties.
When getting the entity schema, the WCF invokes the the default formatter, having the class marked Srializable only (without DataContract) causes all fields to be serialized but no property is. Therefore we’ll see no property but private member.
That’s because the Serializable attribute aimed at binary serialization which serializes the data and not the methods. And data stored in fields and not in properties that are nothing more than methods accessing fields.
Solutions:
1. One solution is to have all service methods exposing the Serializable marked type use the XmlSerializerFormat simply by marking the method’s declaration at the service contract. This will cause the WSDL to be generated using XML serializer and will ignore the Serializable attribute and will serialize all public members including read-write properties.
This solution is good when the entities being exposed are in external DLLs or we can’t change their code for some reason.
2. At the exposed entities, add the DataContract attribute with DataMember attribute on each member we’d like to expose. After doing so, the DataContractFormat (which is the default format being used) will ignore the Serializable attribute and use only the DataMember attribute to serialize the schema.
Summary:
Serializable attribute aimed at binary serialization, which uses all data in the class which stored in fields and not in properties. Therefore, colliding with DataContract serialization it produces an odd behavior of serializing all fields, including private ones and no property.
Using XmlSerializerFormat on the method declarations or DataContract on the very entity being exposed can solve the problem.