Matt Snider JavaScript Resource

Understanding JavaScript and Frameworks

Tuesday, June 17, 2008

Object Subscription Pattern

I have said this before and will probably say it again, “I love CustomEvent“. This was one of the major reasons why I chose YUI as my preferred JavaScript framework. Using CustomEvent you are able to create a subscribable event for anything you might want an event for, and fire that event programmatically.

I find this most useful when leveraging the Module Pattern, as I can keep the event management and all important methods private, while allowing users to define actions at key times during the execution of the code. You can also use this process to pass private data from inside the current Module Pattern to external Object with some degree of control (if absolutely necessary). However, for this pattern to work you need a way to expose the events to the other objects, without attaching every event to ‘this’. For this purpose I use the “Object Subscription Pattern”, which uses one public method and coding conventions for subscribing to events. Here is an example of an Object, Dialog, using both the Module and “Object Subscription Pattern”.

Example 1: Subscript Pattern, Dialog Object

var Dialog = function() { var CE = YAHOO.util.CustomEvent, F = function() {}, that = null; var evt = { beforeCloseEvent: new CE('DialogBeforeCloseEvent', that, CE.FLAT), beforeOpenEvent: new CE('DialogBeforeOpenEvent', that, CE.FLAT), closeEvent: new CE('DialogCloseEvent', that, CE.FLAT), openEvent: new CE('DialogOpenEvent', that, CE.FLAT) }; /** private methods here */ F.prototype = { EVENT_BEFORE_CLOSE: 'beforeClose', EVENT_BEFORE_OPEN: 'beforeOpen', EVENT_CLOSE: 'close', EVENT_OPEN: 'open', subscribe: function(type, func, obj) { var eventName = type + 'Event'; if (evt[eventName]) {evt[eventName].subscribe(func, obj);} else {throw('Invalid event type (' + type + ') inside of Dialog.');} } }; that = new F(); return that; };

Lets assume that Dialog is used to manage a div-based popup system, similar to YUI Dialog. In Example 1 we assume you might care about 4 events: after the dialog opens or closes, and before those same events. For this purpose we have created 4 custom events and attached them to the internal namespace ‘evt’. I always use the FLAT CustomEvent type, because I find the default annoying; the difference is well explained in the YUI documentation. The first parameter of CustomEvent is the name of your events and is very important, because the CustomEvent manager maintains a global list of all names to know what to subscribe to and fire. For this reason, make sure you use a unique name. I prefer the convention of the String NameOfObject + NameOfEvent + ‘Event’. The second parameter will be the execution scope of the callback Functions and I generally pass in a reference to the parent Module Pattern Object. The localized event name, attached to ‘evt’, should follow a convention as well; I prefer to use the String NameOfEvent + ‘Event’ (this will be important in the public ’subscribe’ method).

Now, moving on the the public part of the Module Pattern. I have first exposed some constants that are the event names (NameOfEvent) that I used above. These constants make your public interface easier to read and use by others (and even yourself, when returning to an object you wrote some time ago), since there is no need to search the internal namespace for the available event names. The ’subscribe’ method is just a wrapper for calling the ’subscribe’ method of the internal CustomEvents with the proper parameters. We first test to make sure that the internal event exists, and throw an informative exception otherwise. The second parameter will be your callback function and the third is an optional, additional Object that will also be passed through to the callback method, when your CustomEvent is fired.

That is the gist of the pattern. You have exposed private module events through a public ’subscribe’ method. It is relatively simple to use and setup, just requiring that you use some conventions (mine or your own), with which you can apply the Subscription Pattern to any Module Pattern Object. Lastly, lets go over the actual firing of events. When a point in your code is reach and you want to fire an event, use the following syntax:

Example 2: Firing a CustomEvent

var open = function() { evt.beforeOpenEvent.fire(); /** logic to show the dialog */ evt.openEvent.fire([1, 2, 3]); };

Example 2 could be an internal method of our Dialog object that actually shows the Dialog. Notice, we first first the ‘beforeOpen’ event, followed by the opening logic, then we fire the ‘open’ event. I have also illustrated with the ‘open’ how to pass values through to the callback function. With FLAT CustomEvent types, this should always be an Array and will be the second parameter of your callback function.

posted by Matt Snider at 11:44 pm  

3 Comments »

  1. Thats an neat twist on the module pattern. Never seen it done quite that way before. I am a bit confused though on what the benefit is over just augmenting F with YAHOO.util.EventProvider.

    Comment by bwg — June 18, 2008 @ 5:43 am

  2. Bwg,

    My understanding of EventProvider is that it is a wrapper for CustomEvents that are dynamically created on the fly. I’m not sure the overhead of using EventProvider, but CustomEvent is fairly cheap. In this case you already know what events exist, so you can explicitly create each of them. And you want other objects to know exactly what events are available on the Object.

    Comment by Matt Snider — June 18, 2008 @ 11:45 am

  3. Indeed, custom event rules. But you really have to check out how Google’s Doctype implements custom events. If your new component extends “EventTarget” you can get a common functionality with events by using the same interface.

    Eg:
    // regular events
    goog.events.listen(element, ‘click’, fn);

    // custom events
    var fx = new goog.fx.motion;

    goog.events.listen(fx, ’start’, startFn);
    goog.events.listen(fx, ‘end’, endFn);

    It’s pretty cool actually

    Comment by Dustin Diaz — June 29, 2008 @ 12:21 pm

RSS feed for comments on this post. TrackBack URI

Leave a comment

Powered by WordPress