Creating Component Communication Pipelines with YUI (Decoupling)

By YUI TeamApril 22, 2008
Caridy Patiño Mayea

Caridy, a leading and always-helpful contributor to the YUI community forum, has been working in front of a PC since the nineties. For the past eight years he's been a professional programmer developing LAMP applications for the University of Las Villas where he received his B.S. in Computer Science in 2003, and for several companies around the world. He left his university job in August to pursue an independent consulting career and to follow his passion for open source solutions and agile development.

For the last two years he's been focused on JavaScript as a development platform. Early last year he decided to create an easy-to-adopt YUI extension called "Bubbling Library" as a side project; you can read his YUIBlog introduction to the Bubbling Library here.

Creating complex web applications demands organization and modularization. Modularization introduces a new problem — the comunication pipes between components. This is a serious challenge for developers, as more components and widgets mean more pipelines and more dependencies. In this article, we'll look at a technique to mitigate these issues: Creating an abstraction layer to moderate the comunication between the components and widgets in a web application. This technique is based on YUI and makes use of my Bubbling Library, which is a BSD-licensed superset of functionality that builds on top of the YUI foundation.

Most current YUI components use "Custom Events" as their main communication pipes. This is good. Using custom events you can, for example, know when a YUI Panel is opened or closed, and the components in your application can subscribe a listener to this custom event to know when the Panel's status changes.

YAHOO.example.myPanel.showEvent.subscribe(function(){
// your stuff here...
});

The weakness of this model is that all the components in your application need to know about or have access to the YUI Panel object to subscribe a listener. Even worse, they all need to wait until the YUI Panel instance is created (because that's when the custom event is created and becomes available for subscription), so they need to be aware of the timeline in the application. If you need to introduce a new component in your application, to attach a listener you must wait until the YUI Panel has been created. What happens if you have several panels instead of one? You keep adding more and more listeners, creating more dependencies, and more timeline control.

The solution for this overwhelming amount of complexity is simple and very useful for complex web applications. Those who are accustomed to high-level programming languages, blackbox programmers,and APIs fans will love this implementation for sure.

Decoupling

The idea here is to create components that do not need to know about other components in the application. Each component will catch certain messages (listening events) generated by another component, o create a new message (broadcasting events) based on the received message, and fire the new message to reach all the listeners. We'll have an abstraction layer that will allow us to subscribe listeners for a specific message. For example:

YAHOO.Bubbling.on( 'onPanelShow', function(layer, args) {
// do the job here... });

DecouplingIn this diagram, we have two components that will broadcast a message to the abstraction layer (Custom Behavior Layer), reaching all the listeners for that specific message (keying on the message's name).

Each component will also have three listeners (C1: B1,B2 and B3; C2:B4, B5, B2). In the case of the message B2, both components will be listeners for the same message, and both will be able to react when the message arrives.

What's critical here is that we will be able to subscribe listeners to an undefined event, without worrying about what will fire this type of event or about what else might belistening for the same event. And this new listener will be executed whenever something (a component in your application) fires an event with the same name, in this case onPanelShow.

On the other hand, we will be able to fire an event, without previously defining it. For example:

YAHOO.Bubbling.fire ('onPanelShow', {
param1: value1, param2: value2
});

This call will send a message to all listeners, passing the name of the layer and the literal object (second argument).

We can go one step further, and pass the YUI Panel's reference in the literal object, and the listeners will be able to query the Panel instance's data, check the panel name, etc. For example::

var myPanel = YAHOO.widget.Panel("win", {fixedcenter: true});

myPanel.showEvent.subscribe(function(){
YAHOO.Bubbling.fire ('onPanelShow', {
panel: myPanel, param1: 'value1', param2: 'value2'
});
});

Each time the YUI Panel is displayed, a broadcast message will be fired, the name of the event will be onPanelShow, and the listeners of this type of event will be queried, using the panel, param1 and param2 as references. There are some interesting aspects to this example. You don't need a global reference for the Panel object because there will be no subscribers attached directly to the YUI Panel object. Instead, we will subscribe the listener to the abstract layer (YAHOO.Bubbling). You can have more than one Panel, each Panel will fire the same event, and the listeners will be able to handle the broadcast without knowing anything about the Panel's nature or environment. Finally, each subscriber will be able to define its execution scope, using the 3rd parameter:

YAHOO.Bubbling.on( 'onPanelShow', myListenerFunc, myScope);

Utilized well, this technique gives us the possibility to create a complex web application where each component will have an API to use for communicating with other components without knowing the component itself, or knowing the nature of the component.

Requirements

The Bubbling Library YUI Extension has already implemented this technique, so the requirements are simple:

<script type="text/javascript" src="http://yui.yahooapis.com/2.5.1/build/yahoo-dom-event/yahoo-dom-event.js"></script>
<script type="text/javascript" src='http://js.bubbling-library.com/1.5.0/build/bubbling/bubbling.js'></script>

* Bubbling Core (6k minify version)

Other examples

  • Creating a notification system that will handle application messages — displaying each message in a box on the bottom-right corner of the browser, with different backgrounds, all the components in your application will be able to broadcast a message; the notification system will catch the event and will display the message in the corresponding box, depending on the parameters sent by the sender.
  • Creating a loading mask (just like Gmail) that will display the message every time that the YUI Connection Manager, the YUI Get Utitlity or the YUI Loader starts waiting for a response from the server. In this case, all the components will have access to the Connection Manager, and each component will need to fire the LOADING EVENT every time it uses one of the loading mechanisms.
  • In a web app that supports multiple layouts — every time you switch the current layout, all the web parts will need to be notified in order to readjust the visual interface. All the components can define a listener to the switching action, and all will be notified with the name of the new layout to apply and new css rules isplaying or hiding certain information related to the component.
  • In a webmail system, you need to be notified when a new message arrives or when an unread message has been read. There are some pieces of the application that would be interested in this event (inbox, messenger, the message counter, etc), but the reader panel don't know which of these parts are present on the page.

Conclusions

  • This technique is ideal for a large development teams. It reduces the complexity of the code and minimizes the number of global variables by eliminating the hard pipelines between components.
  • Large applications with many dynamic areas are tricky when you need to control the loading timeline. Using this technique you don't need to worry about the loading timeline.
  • Applications based on this technique are easy to extend.
  • Each component has a simpler API making it easier for other developers to understand and reuse.
  • All the listeners will share the same syntax
    YAHOO.Bubbling.on('onEventName', function(layer, args) {});