Event Aggregation with jQuery

Tuesday, July 7, 2009

As the “write less, do more” library, jQuery garners lots of love for its terseness. The terseness, combined with a rich ecosystem of plug-ins, means I can display my OdeToCode twitter feed on a web page using only 10 lines of code (complete with animation and custom loading message)*.

$(function() {
    $("#getTweets").click(function() {
        $("#tweets").getTwitter({
            userName: $("#screenname").val(),
            numTweets: 10,
            loaderText: "Fetching tweets...",
            slideIn: true,
            showHeading: true,
            headingText: "Latest Tweets",
            showProfileLink: true
        });
        return false;
    });
});

When writing with jQuery there is a tendency to use nested functions and collapse as many responsibilities as possible into a single piece of code. For many web pages this approach is entirely suitable.We aren’t building a space shuttle - it’s just a web page. The code above is responsible for locating the DOM element for events, hooking up a click event, fetching tweets, and locating the DOM element to display the tweets.

Composite UIs

In more complex pages, and particularly in composite pages that are made up from independent pieces, the above approach tends to become brittle, and encapsulation breaks down as independent pieces try to peek into each other’s private business. It’s easy to fall into black hole of JavaScript code that swallows all who come near. A step away from the black hole would be to extract some of the common, reusable functionality into different pieces.

For example, you can separate the piece that knows about DOM elements …

$(function() {
    $("#getTweets").click(function() {
        getTweets($("#tweets"), $("#screenname").val());
        return false;
    });
});

… from the piece that knows about Twitter, and include the pieces independently …

function getTweets(element, screenname) {
    $(element).getTwitter({
        userName: screenname,
        numTweets: 10,
        loaderText: "Loading tweets...",
        slideIn: true,
        showHeading: true,
        headingText: "Latest Tweets",
        showProfileLink: true
    });
}

Now we have a bit of separation. At this point some of us would be inclined to raise the battle cry of the object oriented programmer, and run off to a workstation to design  namespaces, prototypes, constructor functions,  properties – blah blah blah**. But we wouldn’t be creating a greater separation between the pieces of code. All we’d really be doing is creating bigger abstractions that are still tied together as closely as they were when they were simple function objects.

Enter The Aggregator

One of the classes I dig in Prism is the EventAggregator. Eventing is pretty much a required approach to managing a composite UI if you want to stay sane. The EventAggregator makes this easy in WPF and SIlverlight, and includes some thread marshalling tricks behind the scenes as a bonus.

Fortunately, you can do something similar in most major JavaScript frameworks, including jQuery. There are jQuery plugins dedicated to event aggregating, but a simple approach would use bind and trigger with custom events, letting the document serve as the aggregator.

Now we can achieve a greater decoupling between the “I need tweets” action …

$(function() {
    $("#getTweets").click(function() {
        $(document).trigger("fetchTweets", [$("#tweets"), $("#screenname").val()]);
        return false;
    });
});

… and the piece (or pieces) that will respond to such an action…

$(document).bind("fetchTweets", function(e, element, screenname) {
    $(element).getTwitter({
        userName: screenname,
        numTweets: 10,
        loaderText: "Loading tweets...",
        slideIn: true,
        showHeading: true,
        headingText: "Latest Tweets",
        showProfileLink: true
    });
});

It’s easy to layer in additional behavior to the “Get Tweets” button click. We could call a web service to save the user’s preferences. We could cache information. We could add some debugging info …

$(document).bind("fetchTweets", function(e, element, screenname) {
    console.log(screenname);
});

All these things could be done by including separate scripts, and without putting any knowledge of these actions inside the click event handler where the aggregated event begins. Of course, to complete the circle, the code should raise an event when the tweets are retrieved, and let someone else deal with the results.

* Most excellent Twitter plug-in is available from @code_za’s blog.

** I’m not saying that bending prototypes to act like classes is bad, it’s just not the solution to every problem.


Comments
Allison Wednesday, July 8, 2009
Thanks! So many cool things inside jQuery!
Matt Gilbert Wednesday, July 8, 2009
Nice post, definitely an approach I'll be looking into in the future. This line made me stop though:

"I’m not saying that bending prototypes to act like classes is bad..."

I'm struggling to think of an example of when it would be a good choice. Seems like another needless leaky abstraction. Why fight the language implementation?

I'd say that ASP.NET AJAX is flawed and excessively complex because the designers chose not to embrace Javascript, but instead chose to fight it.



scott Wednesday, July 8, 2009
@Matt -

Yes, ASP.NET AJAX is complex. I think the complexity of the framework becomes obvious when you look at usage of the AJAX Control Toolkit. Nobody I know uses ACTK from client script because the code you have to write is borderline revolting. The only real way to use it is with a server-side / form designer view where all the ugliness is hidden behind property windows.

Anyone who wants to use something like a date picking widget from script will opt for an easier to use jQuery plug-in (or something else comparable).

I think a large part of MSAJAX's complexity comes from it's integration with server controls, and perhaps MSFT can one day provide a facade for people who prefer an easier API. Even then it's a large framework to work on top of, which is going to make people uncomfortable.

That being said, I think it's possible to build an API that resembles a C#/Java API (with namespaces, type templates that produce objects with methods and properties, etc) and still have something workable and relatively easy to use.

I think that approach makes sense when you are designing an API with a large surface area. So, something like Google Gears, the various mapping Bing/Google map APIs, etc.

Hmm, maybe I should make a post out of this. I have more to say.
Comments are now closed.
by K. Scott Allen K.Scott Allen
My Pluralsight Courses
The Podcast!