Friday, July 17, 2009

Events - Part 2

Last time I talked about events in ZJS v3. There were many ways I considered for implementing events, but in the end I settled on a nested object to contain the event type definitions and linked that object to both the class and the instance (via the prototype). In other words:


$namespace(foo,
{
Bar : $class(
{
$events :
[
"beforechange",

"change"
]
});
});

var b = new foo.Bar();

assertTrue(b.$events === foo.Bar.$events);

// This is redundant given the first assert, but for clarity:
assertTrue(foo.Bar.prototype.$events === foo.Bar.$events);


The above could have been implemented "directly" in the $class and $mixin logic, but I decided to indulge of bit of architectural overkill and instead I added the concept of Class Plugins. This reduced the footprint of events in the core logic to about 3 lines of code which delegated the work to the plugin.

In the past I had briefly considered making $static work like the above. This would have made it much more direct to reach the statics of your class (this.$static) and would have allowed statics from base classes to be "inherited" due to the prototype chain on the $static object. For various reasons I decided against this, but now, with two uses cases in hand that had the same basic pattern, I figured a more general solution wouldn't be a bad idea. That and the logic in $class and $mixin was already complex enough that I shuddered to think of making it even more so.

With the plugin approach, the extra core code looks like this:


$class.plugins = {};

...

if (name in $class.plugins)
return $class.plugins[name].process(klass, v);


The above test is performed on each member of the object literal being mixed in to a class. The only method that is necessary for a Class Plugin object is the process method that accepts the class (i.e., the constructor function) and the value on which to operate. For convenience, there is a class that implements the machinery (described for chaining and adding to the prototype as well as the class) called zjs.ClassPlugin. The implementation of the "$events" plugin simply derives from zjs.ClassPlugin.

As can probably be guessed, the interesting work of converting an array of strings into something useful for event handling takes place in the plugin. In fact, it was this difference in behavior regarding what to do with the values that pushed me solidly into the whole plugin approach. You see, with events in ExtJS, you call addEvents and pass a bunch of strings. That's it. The string is simply the event name and that is sufficient to declare the fact that an event exists.

In ZJS, these strings are converted into zjs.EventType objects. I'll cover the details next time, but basically the EventType handles subscribing for events as well as creating event instances and triggering delivery of said events. Because these are the key differences between hand rolled events and true DOM events, one can implement EventTypes that do these jobs.

While the zjs.EventType class implements the necessary apparatus for custom events, I have also implemented a proof of concept DomEventType. I wanted to do this to prove to myself that the mechanism can be extended in that direction. It seemed reasonable to pick DOM events because they require a completely different approach to all aspects of their life cycle.

Because of the EventType abstraction, the zjs.EventSource class can provide a consistent interface to any type of event. But that is a story for next time.

No comments:

Post a Comment