ASP.NET AJAX 4.0: Observing updates to POJOs (Plain 'Ole JavaScript Objects)

First of all, if you haven't already done so, download the ASP.NET AJAX 4.0 Preview 3 now and try it out! And after you're done with that, come back here, or there, or there, or there, and let us know what you think and what you like or don't like about it. We love feedback, and it really does have an impact on the product. We wouldn't go through all the trouble of releasing preview bits or presenting these things if it didn't.

So, I thought I would detail one of the interesting yet behind-the-scenes features in the 4.0 preview, because on its own it is pretty useful. I say behind-the-scenes, because it is used by and invented for many of the features in the preview, such as Bindings and the DataView control, but it is also a public API that you can use for your own purposes.

A bit about JavaScript Objects

Objects in JavaScript are associative arrays. If you are familiar with the .NET Hashtable or any equivalent data structure in any other language, it's like that. It's a collection of keys and values -- where keys are strings and values are anything (not accurate to say they are 'Objects' since not everything in JavaScript is an Object, like strings).

var obj = new Object();
obj.foo = "string";
obj.bar = new Date();
obj["abc def"] = [1, 2, 3];

Which of course can be expressed a little more compactly:

var obj = { foo: "string", bar: new Date(), "abc def": [1, 2, 3] };

JSON Web Services

When you call a service that returns JSON, you end up with one of these "plain 'ole JavaScript objects". Plain, because there's nothing 'activate' about it. There's no code that runs within the object when you get and set values, etc. In this world of AJAX frameworks and features, it is just a plain object.

But what if you are plugging that data into a component, which then consumes those values. If you ever change any of the values, the component won't know about it unless you tell it. Wouldn't it be nice if the component could be notified of any changes to the object automatically? That way, for example, if you were rendering out a list of Widgets based on an array of POJO objects containing widget data, you could, say, edit the name of one of the widgets and the rendered list could automatically reflect the updated value, without you having to force it or re-render everything. Doing it manually you may think wouldn't be so bad -- but it means coupling logic with UI, or at least deriving some custom mechanism for decoupling them. But there is no mechanism by which you can be notified of changes to a POJO.

var widget = { name: "widget1" };
renderWidget(widget);
widget.name = "newname";
renderWidget(widget);

A possible solution would be to create an actual class to represent a Widget, and bind to that instead. Then instead of saying widget.name =" newname", you can call a method, like widget.set_name("newname"). The code could then execute a callback to notify anyone interested that there is a new name. The problem here is that the web service you called just returned a POJO. So now you have to convert the POJO to this business class object, or wrap it in one. If there is a lot of data, that could be quite expensive and slow, and it just complicates things.

var widget = { name: "widget1" }; // came from a JSON service
var businessObject = new Widget(widget); // warps or converts it
renderWidget(businessObject);
businessObject.set_name("newname");

Introducing Sys.Observer

The Microsoft AJAX 4.0 preview includes this Sys.Observer class that allows you to be notified of updates to POJO's, provided you use its APIs to modify them. It is named observer of course after the Observer design pattern. Used internally by the live bindings feature and the DataView control, it was necessary to support these features without requiring wrapped business objects, but it is so useful on its own we decided to make it public and independent, so you can use it in your own scenarios too. The above example can be expressed like this.

var widget = { name: "widget1" }; // came from a JSON service
renderWidget(widget);
Sys.Observer.setValue(widget, "name", "newname");

Notice the call to Sys.Observer.setValue(). This sets the "name" field on the POJO, but in a manner that knows how to notify any "observers". Ok -- so, how do you become an observer of the object? The pattern is very similar to the INotifyPropertyChanged interface that Microsoft AJAX Components can implement, but without the interface and bloat of implementing a component. The hypothetical "renderWidget" function here could look something like this:

function renderWidget(widget) {
    Sys.Observer.addPropertyChanged(widget, showWidget);
    showWidget(widget);
}
function showWidget(widget, args) {
    $get('div1').innerHTML = widget.name;
}

The Sys.Observer.addPropertyChanged() method "adds" a "propertyChanged" event handler. You can also stop observing the object by removing the handler with Sys.Observer.removePropertyChanged(). When the component changes because someone calls setValue(), the observers are notified by raising the property changed event for that object. The arguments to the callback are just like a typical AJAX event exposed by an AJAX component -- the first is the "sender", the object that raised the event, and in this case that is the widget, and "args" that is information about the event. The "args" is a familiar type that is already in the AJAX framework -- Sys.PropertyChangedEventArgs. From those args you can get the name of the property which as changed (in this case, "name").

An alternate syntax - giving your POJOs just a little MOJO

The "add" and "remove" prefixes are analogous to the "add_" and "remove_" prefixes that AJAX Components use when exposing events. There is no "_" here though, because these methods don't have the typical signature of an AJAX event adder or remover -- they take the object as a parameter, rather than you calling the adder or remover directly on the object. It may or may not seem to you that the syntax for changing a value on the POJO is a bit verbose. The APIs on Sys.Observer are all static, meaning you have to pass in the object (e.g. 'widget') as the first parameter to all of them. The reason for that obviously is that the object does not have any functions on it which you can execute, it is a POJO after all. However, you can optionally "convert" the object to an observable one, which adds the necessary methods to it for you.

var widget = Sys.Observer.observe({ name: "widget1" }); // make pojo observable
renderWidget(widget);
widget.setValue("name", "newname");

What is the difference you ask? Functionally speaking, there is no difference at all. The two methods are equivalent. In fact, internally the one just calls the other. Practically speaking, converting the object to be an observable object may be convenient in certain cases, but also has the side effect of adding "gunk" onto the plain object. It is no longer a "plain" JavaScript object, since it now has some actual meat on it.  But, just a little bit. Not enough that it loses its "simplicity" that the "P" in POJO gives you. Just a little meat on its legs. I guess that makes it a MOJO, "meaty 'ole JavaScript object" :) But hey, the choice is yours! By the way, none of the internal usage of Sys.Observer calls observe(), so the framework itself will never "gunkify" your POJOs automatically. We made sure of that, we know you are very fond of your POJOs and you don't want some presumptuous framework messing with them and turning them into MOJOs. Ok then.

What about Sys.Observer.getValue()? It doesn't exist. It really doesn't need to -- you can get the value directly off the POJO. No need for the indirection.

What about JavaScript Arrays?

This is great and all, but JSON web services don't return a single widget. They return a lot of widgets, usually. So you are dealing with, uhh, a POJA... a "plain ole javascript array". That's right -- a POJA. You heard it here first. Kind of abusing the POJO term here, but for a good reason -- we COULD have a fat collection type class in the AJAX Framework, but we don't. Arrays have the same problem POJOs have, in that any time they are added to or removed from, there is no logic behind that operation, so there's no way for interested observers to realize when the array has been modified automatically (at least, not without a polling operation).

Observing POJAs

Some of the logical operations you can perform which modify the array using the various JavaScript APIs that exist are add, addRange, insert, remove, removeAt, and clear. The Microsoft AJAX library provides methods named like these as static methods on the Array type. So you have the corresponding APIs on Sys.Observer.

var a = [1, 2, 3]; // POJA
Sys.Observer.add(a, 4);
Sys.Observer.addRange(a, [5, 6]);
Sys.Observer.insert(a, 0, 0);
Sys.Observer.remove(a, 6);
Sys.Observer.removeAt(a, 0); // now [1,2,3,4,5]
Sys.Observer.clear(a);

It's kind of interesting what this support means. I have seen some complaints in the past that the Microsoft AJAX library does not have any "collection" classes. The reason those complaining wanted a collection class was because, well, they wanted to be able to 'observe' the array, even if they didn't realize it. Sys.Observer provides this capability, without the unnecessary bloat of an actual "collection" class that would then only be useful if every component you wanted to use with it knew about it. Since this array is still just an array, it is compatible existing code.

Observing the changes on an array is a little more interesting than on objects. When you add or remove items, you might want to know what index they are on or were on. And in the case of removal, you may want to know what the item was. All of this is available through the new Sys.NotifyCollectionChangeEventArgs class that is passed into the "collection change" event.

function onChanged(a, args) {
    // args is Sys.NotifyCollectionChangedEventArgs();
    var i, j, changes = args.get_changes();
    for (i = 0; i < changes.length; i++) {
        var change = changes[i];
        switch (change.action) {
            case Sys.NotifyCollectionChangedAction.add:
                for (j = 0; j < change.newItems.length; j++) {
                    alert("Added '" + change.newItems[j].name + "' at index " + change.newStartingIndex + j);
                }
                break;
            case Sys.NotifyCollectionChangedAction.remove:
                for (j = 0; j < change.oldItems.length; j++) {
                    alert("Removed '" + change.oldItems[j].name + "' at index " + change.oldStartingIndex + j);
                }
                break;
            case Sys.NotifyCollectionChangedAction.reset:
                alert("Array was cleared.");
                break;
        }
    }
}
 
var widgets = [
    { name: "widget1" },
    { name: "widget2" },
    { name: "widget3"}];
 
Sys.Observer.addCollectionChanged(widgets, onChanged);
 
Sys.Observer.add(widgets, { name: "widget4" });
Sys.Observer.addRange(widgets, [{ name: "widget5" }, { name: "widget6"}]);
Sys.Observer.insert(widgets, 0, { name: "widget0" });
Sys.Observer.remove(widgets, widgets[1]);
Sys.Observer.removeAt(widgets, 0);
Sys.Observer.clear(widgets);

So there you go -- all the information you could possibly need to know about what is happening to the array. Of course, just like turning POJOs into MOJOs, you can optionally turn POJAs into... uhh, MOJAs? I should point out that in addition to the change operations becoming available right on the object/array, so too do the add and remove event handler methods:

var widgets = Sys.Observer.observe(
    [   { name: "widget1" },
        { name: "widget2" },
        { name: "widget3" }
    ]);
 
widgets.add_collectionChanged(onChanged);
 
widgets.add({ name: "widget4" });
widgets.addRange([{ name: "widget5" }, { name: "widget6"}]);
widgets.insert(0, { name: "widget0" });
widgets.remove(widgets[1]);
widgets.removeAt(0);
widgets.clear();

Only, notice the difference in the name. It is "add_collectionChanged", which follows the traditional Microsoft AJAX library event pattern with an "adder" and a "remover". This time, with the "_" since it now follows the correct signature and can be thought of as an actual event on the object rather than a helper method for adding an event to a different object, like the static Sys.Observer.addCollectionChanged() method does.

But wait, there's more!

You may have noticed in the onChanged handler that each change operation can have a list of operations. And, each operation can have a list of "new" or "old" items. The reason for the "new" or "old" items is because of the batch operations you can do in one shot, like addRange(). When you use addRange, there is a single change operation with the new items in the newItems field, for example. But still, that is a single change operation, so why the list of change operations?

Say you have a few different operations you need to perform. You need to remove one item and then add two items. Obviously you must remove the item and then add the new item -- there's no operation that will do both of these steps at once. Any observers of the array are going to be notified of each operation separately, one after the other. If those observers are doing something expensive with that knowledge, you could have a flickering problem. Perhaps the observer renders out the list of items, refreshing the view each time the array is modified. There would be no point in refreshing when you remove an item, only to have to refresh again when you add one right afterwards.

Declaring the beginning and end of an operation

Well, this is a pattern that is actually already employed by the Microsoft AJAX library. When you need to tell a component to hold off on actually doing anything with the changes being made to it, you first call beginUpdate() on it. When you are done, you call endUpdate(), at which point all the changes you made in the meantime are processed at once. The base Sys.UI.Component class provides these beginUpdate and endUpdate methods, but of course, it is up to the implementation of the component to actually honor this behavior. Sys.Observer is not a component, but provides the methods as well, and honors your wishes. Observers are not notified until you call endUpdate().

Sys.Observer.beginUpdate(widgets); // hold your horses man
Sys.Observer.add(widgets, { name: "widget4" });
Sys.Observer.addRange(widgets, [{ name: "widget5" }, { name: "widget6"}]);
Sys.Observer.insert(widgets, 0, { name: "widget0" });
Sys.Observer.remove(widgets, widgets[1]);
Sys.Observer.removeAt(widgets, 0);
Sys.Observer.clear(widgets);
Sys.Observer.endUpdate(widgets); // ok I'm done!
// all operations passed to onChange at once here

The same thing applies to POJOs -- the propertyChanged event is held off until you call endUpdate().

What about Foo.Bar?

Even POJOs can be more complex than what I've been talking about so far. You might have nested POJOs, where the field of a POJO contains another POJO -- a "sub property", if you will. For example, here is a contact POJO.

var contact = { name: "Dave Reed",
    address: { street: "1 Microsoft Way",
        city: "Redmond",
        state: "WA",
        building: 41
    }
};
contact.address.building = 42;

Building 41 is wrong -- gotta change that to 42! But no one will know I've done that. Even if someone is observing the contact object, how will they know the address field has been updated? No problem -- just set the value using the supported "dotted" syntax.

var contact = { name: "Dave Reed",
address:street: "1 Microsoft Way",
            city: "Redmond",
            state: "WA",
            building: 41 }
};
Sys.Observer.addPropertyChanged(contact, contactChanged);
function contactChanged(contact, args) {
    if (args.get_propertyName() === "address") {
        alert(contact.address.building);
    }
}
Sys.Observer.setValue(contact, "address.building", 42);

Even though the address field of the contact has not itself changed, a value on it has. That qualifies as a property change for the address field, so it shows up as a change to the address, not to the 'building' field. That's important, because if I want to observe changes to the contact object as a whole, I wouldn't always know that a field on contact may have sub-fields. It wouldn't be nice to have to observe changes to address directly, too. If I want specific information on what fields of the address changed, well then I can observe that directly instead.

Not just for Objects and Arrays

You can use these APIs on more than just objects or arrays. You can use it on DOM Elements, or on AJAX controls, components, and behaviors. Any AJAX class, or even components from other libraries. You can even use it on the global "window" object, which means you could use it to monitor changes to global variables. You could combine the observation of arrays and objects, for example, to be notified of any changes to an array OR any of the objects it contains. The caveat of course is that you must use Sys.Observer's APIs to change the value rather than doing it directly.

Remember our discussion of JSON web services returning plain data, and you having to wrap that data or convert it to get this functionality? Well here you go -- and one of the great things about the approach this technique uses is that you don't have to pay the cost for wrapping or converting objects that you don't actually need to 'observe'. If you don't observe an object, and you don't change its value, there is 0 cost. If all you do is change its value with setValue(), but no one is listening, the overhead of checking for any registered observers is very tiny.

All of this has been a pretty hypothetical discussion, with no real concrete examples of how you might use this feature. Well, I'll put together some examples if the interest is there. But a good place to start is to take a look at the Microsoft AJAX 4.0 Preview 3 Live Bindings feature, and realize that it is the flux capacitor, err, I mean, this functionality that makes that feature possible. A great place to get an idea of what live bindings has to offer is this excellent example.

Happy Coding!

10 Comments

  • I'm somewhat confused by the array use case. It seems to me that it would be desirable to make an array observable, but not force a change to the way the array is manipulated by client code. If people are used to using the static Array methods, now they have to remember to use the corresponding Observer methods instead? That doesn't seem so great.

  • sorry i get confuse, about term POJOs for most developers means Plain Old Java Object, by the way nice post :)

  • John -- the purpose of the Observer isn't to change the way you work with javascript objects. So it's not that you should now always remember to use Observer instead. You only use it when it is appropriate. Currently the Array statics are just convinence helpers that let you do stuff to arrays that would otherwise involve some boilerplate code, I'm not sure we want to inject actual meaning into them. It is very clear when you use Observer what it is for. But that is interesting feedback, something we can mull over.

  • Angel -- that is where the term originated, but it's been in use lately in the AJAX world too.

  • I guess my comments about the arrays come from my desire to use an Observable array this in the same way I would use ObservableCollection in WPF; that is an implementation detail when building components. I want my component to expose a array/collection of something to clients, but the component needs to be notified when the client code messes with the array. I suppose the question is what API does the client use to manipulate the array; the MOJA you describe I guess is that API, but I guess it just seems a little odd not to have some contract (i.e something like ICollection) that defines the operations. Maybe I'm thinking too much like a C# programmer though...

  • John -- Like a C# developer, yeah, that is part of it. On the client it is more favorable to have simple solutions. Javascript is very loose compared with C#, and making things 'tight' involves overhead that is seen by many as just unnecessary overhead, and in many cases, is easily ignored anyway. No matter what we couldnt stop you from access fields or the array directly. The only fool proof solution would be if objects and arrays were observable natively.

  • Nice. I thought of the same thing years ago, but I never bothered to write an implementation.

  • "POJO" == Plain Old *Java* Object
    "POJO" != 'Plain 'Ole JavaScript Objects'

    Don't overload acronyms!


    ================
    Proposal:

    "POJSO" ==> 'Plain 'Ole JavaScript Objects'

    Mmm . . . OK?

  • > (not accurate to say they are 'Objects' since not everything in JavaScript is an Object, like strings)

    Not exactly true. Try this:

    "I'm a string".toLowerCase().length

    The 'string' literal is always treated as a String object, so there is no difference between the two.

  • Jason -- I know you can do that, but that doesnt make it an Object as far as what 'Object' means in javascript.

    Try this:

    alert(typeof("abc"));

    Or

    alert("abc" instanceof Object);

Comments have been disabled for this content.