In my previous blog posts I showed possible solutions to WinJS observables pitfalls. Let's combine both into the one, final solution.

 

Goal

Just repeat again what's our goal in BDD style:

AS a developer

I WANT to be able to define observable properties on the class as well as other class parts: get/set properties and methods and I don't want to pay any unnecessary performance penalties

SO THAT the observable object will be able to notify listeners that the property has changed

 

Solution

The previous both solutions were not perfect solution. Manual approach was flexible very much but manual, latter solution doesn't handle methods nor get/set properties correctly. Let's combine them to solve both issues:

  1. it will use WinJS as much as possible
  2. handle class methods correctly
The idea is that:
  1. we mark the properties which we need to be observable and 
  2. mix observable capabilities into class
The desired result is:
 
var Person = observableClass(function () {
}, {
    name: observableProperty("Frantisek Kaduk"),
    _year: 1234,
    year: { get: function () { return this._year; } },
    getName: function () { return this.name; }
});
 
 
 
Let's start with marking the property as observable:

function observableProperty(defaultValue) {
    return {
        value: defaultValue,
       observable: true,
        writable: true,
        enumerable: true
    };
}
 
 
 
The solution uses Javascript posibilities to define the property metadata. It's similar to .NET attributes. The method creates the property definition with the custom attribute 'observable'  which will be used later.
 
 
 Let's look at the class definition helper method:

function observableClass(ctor, instanceMembers, staticMembers) {
    var properties = extractObservableProperties(instanceMembers);

    var baseClass = WinJS.Binding.define(properties);
    return WinJS.Class.derive(baseClass, function () {
        baseClass.call(this);
        ctor.apply(this, arguments);
    }, instanceMembers, staticMembers);
}


The method uses WinJs.Binding on the filtered "observable"-only properties. 
 
function extractObservableProperties(shape) {
    var props = {};
    Object.getOwnPropertyNames(shape).forEach(function (k) {
        var descr = shape[k];
      if (descr.observable) {
            delete shape[k];
            props[k] = descr.value;
        }
    });
    return props;
};


After running the sample (attached to the blog) the output is as expected:
 
person: name has changed to Frantisek Kaduk
name is: Fero Kaduk
year is: 1234
person: name has changed to Fero Kaduk
 
That's all.
 
The next step is to add support for deriving the classes.