thoughts on coding

June 2, 2012

Observables and data-binding in action (Part III)

Filed under: Javascript, WWA — Tags: , , , — Frantisek @ 11:03 pm

Observables and their usage in binding

The typical scenario where observables’ features are leveraged is data-binding (binding the observable objects to html element – in other words model/viewmodel to view).

Declaration syntax

WinJS establish data-binding to UI in the following form:

<div data-win-bind="targetPropertyChain:sourcePropertyChain [customBindingFunction]" >

This was a declaration with the following meaning:

  • targetPropertyChain: declares a target of the databinding. It’s property name on the HTML element. Example: innerText. It supports also the chain of the properties, e.g. winControl.content.
  • sourcePropertyChain: declares a source of the databinding. It’s property name chain on an object (also called databinding context) whose data are used for binding. Example: address.street, name, etc.
  • customBindingFunction: it’s an optional parameter and declares a custom databinding function which replaces the default databinding function. There are several examples of it developed in MvvmJS. WinJS comes with two binding functions:
    1. default: it’s default oneway databinding function which binds a property from databinding context into a property on HTML element initialy and any time property on context object changes.
    2. onetime: it’s the function which bind the property from databinding context into element only once and only once.

Usage examples

<div data-win-bind="innerText:name" >

It’s the most simple binding use-case and declares binding from datacontext.name to targetElement.innerText.

<div data-win-bind="VISIBLE:isEmpty toggleClass" >

This defines the binding with the custom binding function defined as an extension in MvvmJS. The binding function toggleClass adds class VISIBLE to the element CSS class attribute in case datacontext.isEmpty is true.

<div data-win-bind="innerText:person.homeAddress.street" >

This defines binding street of the home address of the person accessible as a property on datacontext object to the innerText property on the target HTML element.

<div data-win-control="MvvmJS.UI.TemplateSelector" data-win-bind="winControl.content:person" >

First, it declares control MvvmJS.UI.TemplateSelector control (a custom WinJS UI control) over DIV. Then there is a databinding of datacontext.person to content property of winControl attribute set on the target DIV element. Note: it’s usual practice/pattern that UI control sets attribute winControl on the element to control instance.

Data-binding in action

That was just the first part of the story: declaration. Now, lets make the databinding alive.

WinJS introduces WinJS.Binding.processAll(root, dataContext, ..other parameters..) method which:

  1. searches for any element with data-win-bind attribute on the HTML element tree starting with root (optionaly skipping root) and
  2. then process the binding declarations.

WinJS.Binding.processAll method is usually called from JS page or fragment file.

Example

(function () {
    "use strict";

    WinJS.UI.Pages.define("/views/dummyView.html", {
        element: null,

        ready: function (element, options) {
            this.element = element;
            element.winControl = this;
            this.options = options;

            var vm = new MvvmJS.Samples.DummyVieModel();
            WinJS.Binding.processAll(element, vm);
        },
    });
}());

The example above is code-behind of the view DummyView which runs the binding processing in the ready event handler (raised when the page is rendered and ready to be used). In the ready event it instantiates DummyViewModel (please note a convention DummyView ~ DummyViewModel).

In case of an enterprise application it would be better to generalize the approach and define a base class with calling the binding process method from its ready method (this also brings MvvmJS).

Property chain subscriptions

There is one very usefull feature of WinJS databinding. WinJS databinding leverage observables and adds there property chain subscription of any property in sourcePropertyChain.

Let’s explain it on the example in BDD (behavior driven development) style:
Given: there is the following binding declaration

<div data-win-bind="innerText:person.address.street" >

When: person OR address OR street changes

Then: the binding is reprocessed and div’s innertext contains new value. This is very usefull feature in complex scenarios.

Comparing to manual approach

In case of manual approach it would be necessary to:

  1. initialy read the property and set the HTML element property with the value
  2. subscribe to any property chain change and handle the it with setting the new value

Manual approach implies more boilerplate and repetitive code which increase complexity of the result JS code.

In the next part we will look into custom binding method and extensions introduced in MvvmJS.

May 27, 2012

WinJS Observables in Examples (Part II)

Filed under: WWA — Tags: , , — Frantisek @ 1:10 pm

Non-obserserable class

In my previous part I wrote about Observable introduced in WinJS. It was more about what they are and how to define them better. In this part I’d like to explain their usage and all explained on examples. Let’s start with sample class:

    describe("non-observable class", function () {
        it("DTO class", function () {
            //arrange
            var Person = WinJS.Class.define(function () {
            }, {
                Name: null,
                Birthday: null,
                Age: null
            });

            var sut = new Person(); // sut = subject under test

            var events = [];
            // subscribes to any change in the property Name
            sut.bind("Name", function(newValue, oldvalue) {
                events.put(newValue);
            });

            //act
            runs(function () {
                sut.Name = "dummy person";
            })

            //assert
            waitsFor(function () { return event.length != 0 });

            runs(function () {
                expect(event.length)
            })
        })

The result is:

Observable DTO object

The previous example defines class Person which has three direct properties. The instances of this class do not inform the consumer about their changes and it’s not possible to subscribe to any changes there. In other words, we are not able to subscribe to any event.It’s just simple class (in this case also so called DTO class). In case of DTO classes (classes used as data holders), it’s possible to call WinJS.Binding.as(object) in order to transform them to observable.

        it("observable DTO instances ", function () {
            var events;
            runs(function () {

                //arrange
                //define  non-obserable class
                var Person = WinJS.Class.define(function() {
                }, {
                    Name: null,
                    Birthday: null,
                    Age: null
                });

                // create new class instance (object)
                var sut = new Person(); // sut = subject under test
                
                // transforms object into observable object
                sut = WinJS.Binding.as(sut);

                // subscribes to any change in the property Name
                sut.bind("Name", function(newValue, oldvalue) {
                    if(oldvalue !== undefined) {
                        events.push(newValue);
                    }
                });
                events = [];
                //act
                sut.Name = "dummy person";
            })

            waitsFor(function() { return events.length !== 0; })

            runs(function () {

                //assert
                expect(events[0]).toBe("dummy person");

            })

        })

In the above example there is class Person and class instance named ‘sut’. The class instance is then transformed into observable object (under the hood, sut is wrapped with dynamically generated observable wrapper). Then any change on the class property triggers the property change event. Subscription to the particular property change can be realized calling bind(propertyname, callback) method (more about it later).

Observable DTO class

Disadvantage of the above approach is that it’s necessary tranform every class instance to the observable version. In addition, it’s necessary to call property changes only via wrapper object ‘sut’ and not on the original object, otherwise no updates are raised. This is very common issue. Solution is using WinJS.Binding.define() method.

        it("defines observable DTO class", function () {
            var events;
            runs(function () {

                //arrange
                //define  non-obserable class
                var Person = WinJS.Binding.define({
                    Name: null,
                    Birthday: null,
                    Age: null
                });

                // create new class instance (object)
                var sut = new Person(); // sut = subject under test

                // subscribes to any change in the property Name
                sut.bind("Name", function (newValue, oldvalue) {
                    if (oldvalue !== undefined) {
                        events.push(newValue);
                    }
                });
                events = [];
                //act
                sut.Name = "dummy person";
            })

            waitsFor(function () { return events.length !== 0; })

            runs(function () {

                //assert
                expect(events[0]).toBe("dummy person");

            })

        })

The example above defines directly observable class Person. All class instances are then observable by default.

MVVM and Observables

That’s all fine but all works only on pure DTO classes. WinJS introduces observables especially for binding purposes. The best architecture model leveraging observables is M-V-VM (Model View ViewModel). The binding is used between V and VM – in other words, beween UI and ViewModel.

ViewModel has the following purposes:

  1. prepares data for View
  2. react to actions triggered from the View

ViewModel classes are full classes, not just DTO classes. ViewModel has properties (both access and value), methods (private, public), etc.

But WinJS-observabes has the following problems:

  1. it doesnt support observables on classes with accessible properties with getters/setters
  2. it wraps also methods into observables
  3. it’s not possible to define what properties are observable

The solution is using MvvmJS.Class.define(constructor, prototypeMembers, observableMembers, staticMembers). It defines normal class as with WinJS.Class.define but it adds few usefull capabilities:

  1. it enables us to specify (explicitly as third parameter) properties which we want to have with observable capabilities
  2. adds event capabilities (add/removeEventListener, dispatchEvent)

Fully observable class using MvvmJS.Class.define()


        it("defines observable full class", function () {
            var events = [];
            runs(function () {

                //arrange
                //define  non-obserable class
                var Person = MvvmJS.Class.define(function (name) {
                    // constructor  

                    this.Name = name;
                }, {
                    // non-observable members
                    Name: null,
                }, {
                    // observable members
                    Birthday: {
                        get: function () {
                            return this._birthday;
                        },
                        set : function (value) {
                            this._birthday = value;
                            this.Age = new Date().getYear() - this._birthday.getYear();
                        }
                    },
                    Age: null,
                });

                // create new class instance (object)
                var sut = new Person(); // sut = subject under test

                // subscribes to any change in the property Name
                sut.bind("Name", function (newValue, oldvalue) {
                    if (oldvalue !== undefined) {
                        events.push(newValue);
                    }
                });
                sut.bind("Birthday", function (newValue, oldvalue) {
                        events.push(newValue);
                });
                sut.bind("Age", function (newValue, oldvalue) {
                        events.push(newValue);
                });
                events = [];
                //act
                sut.Name = "dummy person";
                sut.Birthday = new Date(2000,1,1);
            })

            waitsFor(function () { return events.length > 1; })

            runs(function () {

                //assert
                expect(events[0]).toBe(12);
                expect(events[1].toString()).toBe(new Date(2000, 1, 1).toString());

            })

        })

There is the class Person with non-observable property Name and two observable properties (Age is value propety, Birthday is accessible (get/set) property). Setting Name doesn’t raise any notifications. Setting Birthday ends in setter method which sets the private _birthday property, calculates age and sets Age property. Both Age and Birthday properties trigger change notifications which are captured and stored in events array.

Using “propertychanged” event

I mentioned that MvvmJS.Class.define() adds also the event capabilities into the class in addition to notifications for binding. There are several reasons for this:

  1. when it’s necessary to be notified about the property change synchronously (immediately). By default the standard change property notifications (added by WinJS.Binding, also called binding notifications) are raised “asynchronously” – which means they are raised using setImmediate() function which queues the raising into the windows message queue. It’s good as Javascript engine has only one thread but sometimes it’s necessary to react immediately.
  2. when it’s better to have only one event and one event handler to handle any property change on the object. When using standard binding notifications added by WinJS it’s necessary to explicitly subscribe to each property change. So last example would then look like the following:
        it("defines observable full class with property change notifications", function () {

            //arrange
            //define  non-obserable class
            var Person = MvvmJS.Class.define(function (name) {
                // constructor  

                this.Name = name;
            }, {
                // non-observable members
                Name: null,
            }, {
                // observable members
                Birthday: {
                    get: function () {
                        return this._birthday;
                    },
                    set: function (value) {
                        this._birthday = value;
                        this.Age = new Date().getYear() - this._birthday.getYear();
                    }
                },
                Age: null,
            });

            // create new class instance (object)
            var sut = new Person(); // sut = subject under test

            var events = [];
            // subscribes to any change in the property Name
            sut.addEventListener("propertychanged", function (args) {
                var propertyName = args.detail;
                events.push(propertyName);
            });
            //act
            sut.Name = "dummy person";
            sut.Birthday = new Date(2000, 1, 1);

            //assert
            expect(events[0]).toBe("Age");
            expect(events[1]).toBe("Birthday");

        })

Summary

MvvmJS.Class.define() method enables us to define classes without any limitations, so it’s ideal to use it in M-V-VM architecture scenarios. Specifying observable properties explicitely has positive performance impact because overhead added due to observable capabilities is added only to required parts.

All examples can be downloaded from http://mvvmjs.codeplex.com/

Blog at WordPress.com.