I've been, effectively, prototyping a client-side pub/sub solution for
Tapestry with one of my client projects.

The goal is to reduce the abuse of DOM events or other complicated
structure.  If two components need to work together when they are both
present in the page, it can be difficult to orchestrate when those
components render at different times (due to Ajax) or are nested
within other components (making it hard to obtain a client-side id to
work with). Having behaviors loosely coupled, using well-known topic
names, is a major part of the solution for this.

Topics names are of the form "tapestry:zone-updated/myZoneId" or
"app:master-category-changed".  The convention I'm coming up with is
that topic names start with a prefix ("tapestry:" for the framework,
or "app:" for the application), then a name, then optionally a
client-element id.

Message publication is by topic name, and there's a bubbling system: a
message published to "foo/bar" can be subscribed to with "foo/bar" or
"foo".  This will allow, for example, code to be aware of any form
that's about to submit, or any Zone that's about to update, etc.

The topic message payload (or just "message") is any JavaScript
object, defined by the code that publishes to the topic.  It is passed
to any listener functions listening to the topic.

Part of the code is in 5.3.0, part of it is evolving inside my client
project and will be merged into 5.3.x.

In any case, lots of it is working out nicely. I have some very
sophisticated logic on the client side and the pubsub system makes
things possible that otherwise would not be.

However, I do have a concern.  Although its easy for a listener to
unsubscribe itself, what I'm struggling with is when a topic is
associated with a DOM element (which is often the case).  I would like
to have a means of recognizing that a DOM element (or tree of DOM
elements) has been removed from the DOM, and to automatically destroy
topic publishers and listeners related to that DOM element.

Failure to do this will likely result in memory leaks inside
long-running pages, and potentially to client-side exceptions as
listeners perform updates to DOM elements that are no longer attached
to the document.

One option I'm considering is to make topic names simple strings, but
to always associate them with a DOM element.  I.e.:

var publisher = T5.createPublisher("tapestry:form-validate", myForm);
// createPublisher() returns a publisher function.
// The return value of the publisher is an array of the return values
of all listener functions.

T5.sub({ topic: "tapestry:form-validate", element: myForm}, function()
{ ... }); // to match message published for myForm
// The return value of sub() is a function used to unsubscribe the
listener function from the topic. This can be used for
// "listen once" scenarios.

T5.sub("tapestry:form-validate", function() { ... }); // to match
messages published for any form

var result = publisher({ foo:bar, biff:baz}); // Send the object to
listener functions
// Get back an array of return values.

This would replace and formalize the "foo/client-id" pattern I discuss above.

The pub/sub approach above could combine with some code that would
iterate over elements being removed form the DOM and cleanup the
publishers/listeners mappings.  Part of this code already exists, to
clean things up for IE (breaking apart the DOM and JS to prevent
memory leaks).

As I write this, I'm really liking the idea of always linking topics
to elements; listener functions could receive two parameters: the
payload message, and (second & optional) an event object that would
include, among other things, a reference to the element for the
message.

In the larger scheme of things, appling this pub/sub structure will
have a lot of benefits.  It effectively provides more "seams" on the
client-side where logic can be injected, which should lead to less
monkey-patching. I also see it as a great mechanism for dealing with
Cometd/Websockets, where the server-side can publish events to the
client-side.

My personal plan is to start introducing the T5 namespace and pub/sub
features in 5.3 and go a bit more whole-hog in 5.4.  In fact, the
"theme" of 5.4 could be JavaScript: breaking the ties on Prototype,
pub/sub, and Websocket support.

So, I'm interested in general thoughts on the approach, and also some
insight into the memory leak issue.  It would be great if there was a
bubbling DOM event that we could use for this purpose. I don't think
there is.

-- 
Howard M. Lewis Ship

Creator of Apache Tapestry

The source for Tapestry training, mentoring and support. Contact me to
learn how I can get you up and productive in Tapestry fast!

(971) 678-5210
http://howardlewisship.com

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to