thoughts on coding

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/

Advertisements

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: