Friday, July 20, 2007

Aspect-Oriented... JavaScript?

Or, how to create an event handler where there wasn't one.


Some background: I wanted to open a window (a RadWindow actually from the Telerik RadAjaxManager) and refresh a portion of the screen (a RadAjaxPanel) when the window was closed. Unfortunately, the developer of the window library (Telerik) didn't anticipate this by creating an OnClose property of the window class. In fact, they had not event handlers at all in the client-side JavaScript object model for the window.


So, what to do? How can I know when the window is closed? Well, a procedural programmer might simply poll the window handle periodically until the window is closed. An object-oriented programmer would decry the lack of an observer pattern in the 3rd-party's object model, and fall back to wrapping the procedural approach in a bunch of classes. What about the functional programmer? The answer looks something like this:


whenReturning(getWindow(),'Close', refreshScreen);


In the code above, getWindow returns a window object reference, 'Close' is the method called to close a window, and refreshScreen is a function object that we want to call when the close method returns. Take a moment to drink that in... What this code describes is a standard way to create your own event handler. In plain English, what the code above does (I'll show you how in a moment) can be stated as, "whenReturning from the getWindow().Close method, refreshScreen". That's powerful.


The semantics are different from wiring up event handlers; we're not making an assignment to an "OnXXXX" property of the object. In the .NET parlance, we're not making a "type safe assignment of a delegate instance to a multicast delegate". To a functional programmer, this semantic is more natural. To folks acquainted with aspect-oriented programming, this may also seem familiar. The function "whenReturning" is providing advice ("refreshScreen") at a join-point ("getWindow().Close"). In fact, this is the perspective of the article where I first picked up this technique.


In a blog entry "Aspects in JavaScript", William Taysom describes how to implement AOP advice using some of the features of JavaScript, including closures, type mutability, and dynamic scoping. (He's a fan of dynamic languages.) The article assumes a lot of context and understanding on the part of the reader regarding these language features.



If you're looking for a quick solution, you might have better luck using dojo.event.connect. Here's a snippet from their site, "[...] lets say that I want exampleObj.bar() to get called whenever exampleObj.foo() is called. We can set this up the same way that we do with DOM events:"


dojo.event.connect(exampleObj, "foo", exampleObj, "bar");


So, if you need a quick win here, go grab dojo and use their event connector. If you're new to JavaScript as a dynamic, functional language, you would do well to learn about how functions are first-class objects, all of an object's properties are an index into a dynamic associative array, inheritance through prototypes, and how lexical scoping works with "this" and closures. At the very least, to understand the code in the "Aspects in JavaScript" article, you need to understand function call dispatching in JavaScript.


Building off of William Taysom's work, we could extend this advice to all instances of the window (RadWindow) object by modifying the prototype. That may be the subject of a future post. Until then, dear reader, best wishes.

No comments: