Bringing Valuable Information to DevOps Professionals

DevOps Journal

Subscribe to DevOps Journal: eMailAlertsEmail Alerts newslettersWeekly Newsletters
Get DevOps Journal: homepageHomepage mobileMobile rssRSS facebookFacebook twitterTwitter linkedinLinkedIn


DevOpsJournal Authors: Dalibor Siroky, Liz McMillan, Stackify Blog, John Rauser, XebiaLabs Blog

Related Topics: Facebook on Ulitzer

Facebook: Article

Loosely Coupled Flex and JS Widgets

Integrating Web Widgets With a Publish/Subscribe Architecture

It has become very popular to build web sites that use and embed widgets from various providers. For example, MySpace and Facebook let their users easily embed various JavaScript widgets and Flash-based applications and videos onto their personal pages. In most cases, the widgets act in a standalone fashion, unaware of the other widgets contained on the same page. Standalone widgets are fine for simple portal pages but for more targeted applications some level of interaction among the components on the page is needed. When such interactions are needed, they are usually achieved through tightly-coupled point-to-point integrations between the widgets which can make it difficult to add new widgets or remove widgets from the page. This article will show how a publish/subscribe architecture can be used to integrate Flex and Javascript widgets embedded on a page in a loosely-coupled manner.

Example and Architecture

For this article a simple example that integrates a calendar control from YUI with a Flex-based datagrid that displays a list of appointments will be used to demonstrate the architecture. These two widgets are integrated so that when a date is selected on the calendar the datagrid displays the list of appointments on that date (if any) and when an appointment is selected in the datagrid the date of the appointment is highlighted on the calendar. This is accomplished through a loosely-coupled interaction model where the widgets don’t directly communicate with each other but instead send messages to and respond to messages from a message-bus located in the page that contains these widgets, illustrated in figure 1 below. This works much in the same way as large-scale distributed systems that use a publish/subscribe architecture allowing publishers and subscribers (user interface widgets in this case) to be added to and removed from the system without directly affecting each other.

Figure 1: Publish/subscribe architecture for web widgets

Design

The design for this system is illustrated in the class diagram shown in figure 2. The EmbeddingPage is an HTML page that contains the ApptmtListWidget that is written in Flex, a JavaScript-based CalendarWrapper component that wraps a YUI (Yahoo! User Interface) CalendarWidget, and the JavaScript-based PageBus component from TIBCO. The EmbeddingPage also has a JavaScript-based PubSubUtils component that contains utility functions for obtaining the Flex widget embedded on the page and subscribing the widgets to the PageBus as well as constructor functions to create the JavaScript-based representation of the messages that are sent between widgets embedded on the page. In this example, we are using a simple structure for the messages, shown in the diagram as the PubSubMsg consisting of a msgType and an array of MsgParam’s which are just name-value pairs.

Figure 2: System class diagram

The ApptmtListWidget is a simple Flex application with a datagrid that displays a list of appointments. It has three functions: init(), used to set up the Flex application so that it can expose functions to JavaScript; publishToPageBus(), used to publish events occurring within the widget as messages to the PageBus; and callback(), which is called when PageBus messages are sent to the ApptmtListWidget so that it can appropriately update the datagrid based on calendar events. The ApptmtListWidget also uses the ActionScript equivalent of the PubSubMsg and its array of MsgParam’s to send messages from Flex to other widgets embedded in the page over the PageBus. These objects are automatically converted to JavaScript as they cross the Flex-Javascript border.

The CalendarWrapper is a simple JavaScript component that wraps the YUICalendarWidget so that it can participate in the publish/subscribe architecture that is used for widget interaction. The callback() function is invoked by the PageBus when there are messages sent to the bus that match the CalendarWrapper’s subscription, e.g. when messages are sent to the bus from the ApptmtListWidget. This allows the CalendarWrapper to appropriately update the YUICalendarWidget based on appointment-selected events from the ApptmtListWidget. The publishToPageBus() function is used to publish calendar events, e.g. a date being selected, as messages to the PageBus so that the ApptmtListWidget can appropriately respond to them.

The PageBus is a JavaScript component from TIBCO that provides a publish/subscribe message bus that enables JavaScript components contained in a Web page to broadcast and listen for events and messages published on topic names. It has a rich API to support these capabilities but in this example we just use the publish() and subscribe() functions. The publish() function is used to send events from the widgets on the page as messages onto the PageBus. The subscribe() function is used to subscribe the widgets to the PageBus so that they can receive these messages and thus indirectly (i.e. in a loosely-coupled fashion) respond to events from each other.

Implementing the Embedding Page

The source for the EmbeddingPage and everything else can be downloaded from the link in the Resources section of this article. It uses a bit of JavaScript code to initialize the page when it is loaded, shown below in listing 1:

Listing 1: Initialization code for the embedding page

The first thing that is done is to create an instance of the CalendarWrapper object to embed the wrapped YUI calendar on this page (line 4). Although not shown in listing 1, the Flex-based ApptmtListWidget is embedded on the page using the standard HTML <object> and <embed> tags as described here.

Next, the widgets are subscribed to the PageBus so that they can start receiving messages published onto the bus. On line 8, the calendar is subscribed to the "apptmEvents" topic so that it can receive messages published to the PageBus from the ApptmtListWidget. On line 12, the ApptmListWidget is subscribed to the "calendarEvents" topic so that it can receive messages published from the calendar widget. The function subscribeWidgetToPageBus() from the PubSubUtils (shown below in listing 2) is used to assist with subscribing the widgets to the PageBus.

Listing 2: Utility function to subscribe widgets to the PageBus

This function uses a JavaScript closure so that when the PageBus invokes the callback function, it invokes it on the correct instance of the widget. Then it invokes the subscribe() function on the PageBus, passing in the reference to the inner callback function that is created along with other required parameters of the subscribe() function as described below:

  • topic--the specific topic to subscribe to; when messages are published to this topic, all subscribers that have subscribed to this topic will get a copy
  • scope--the context of the callback function, i.e. when the JavaScript this keyword is used in the callback function, it will point to this object; instead of using a closure as described above, this parameter could also be used to pass in the specific instance of the widget
  • callback--this is the callback function that gets invoked when a message is published on a topic that matches the subscription
  • subscrData--user-defined data that can be passed from the subscriber to the callback function

More information on the PageBus API can be found here.

Wrapping YUI Calendar

As mentioned earlier, this example uses the Calendar component from YUI and some glue code is needed to allow it to interact with the other widget in this publish/subscribe model. There are couple ways to do this: 1) directly modify the JavaScript source code from Yahoo!; or 2) treat it as a black box and wrap it with a wrapper object. As already indicated in the design earlier, this example uses the second approach and wraps the Calendar component from YUI with the CalendarWrapper object. The constructor function for the CalendarWrapper is shown below in listing 3.

Listing 3: Constructor for CalendarWrapper

The constructor takes two parameters: name, which is used as the id for the HTML table that gets created when the YUI Calendar component is created; and container, which is the id of the div element where the component should be inserted in the page. Lines 5 and 6 add the callback() and publishToPageBus() functions as methods to the CalendarWrapper. The instance of the YUI Calendar component that will be wrapped by the CalendarWrapper is instantiated in line 8. YUI Calendar also uses an event-driven model to allow other components to be notified of interesting events that occur on the Calendar, e.g. a user clicking on a date. Line 9 subscribes the CalendarWrapper’s publishToPageBus() method to the YUI Calendar’s selectEvent so that when a date is selected on the calendar, that method will get invoked and publish messages to the PageBus that contain the selected date. This will allow other widgets to respond to such events without having to directly subscribe to the YUI Calendar. Line 11 simply renders the YUI Calendar after it’s been properly configured.

Line 8 in listing 1--the initialization code of the EmbeddingPage subscribes the CalendarWrapper to "apptmEvents" on the PageBus. As part of the subscription, a reference to CalendarWrapper’s callback() method is provided to the PageBus. The source for the callback() method is shown below in listing 4.

Listing 4: CalendarWrapper’s callback() method

The parameters of the callback() function are defined by PageBus and described below:

  • topic--the topic on which the message was published; it should match the topic in the subscription
  • msg--a copy of the message that was published to PageBus
  • subscriberData--user-defined data passed from the subscriber to the callback function

The implementation of this callback() method simply takes the date that was passed in the message, pages the YUI Calendar to display the month of that date and highlights the specific day on the YUI Calendar. Lines 6, 7, and 8 assume that the messages are based on a standard structure containing an array of params, each of which consists of a name-value pair as described earlier. Standard message types, whether they are as simple as in this example or based on complex XML structures, are a key part of any publish/subscribe architecture. Without standard message types, subscribers have to know exactly how the publishers have structured the messages that they’ve sent--defeating the purpose of the loose-coupling offered by the publish/subscribe architecture.

In addition to the callback() method, the CalendarWrapper also has a publishToPageBus() method that allows it to respond to events from YUI Calendar and send them as messages onto the PageBus. A reference to this method is provided to the YUI Calendar in the CalendarWrapper’s constructor function when it subscribes to the calendar’s selectEvent (line 9 in listing 3). The source for the publishToPageBus() method is shown below in listing 5.

Listing 5: CalendarWrapper’s publishToPageBus() method

Since the publishToPageBus() method is a callback function for YUI Calendar’s event-handling mechanism, the parameters to the function are defined by YUI and are described below:

  • type--the type of event that occurred, i.e. the selectEvent
  • args--array of the selected dates
  • obj--user-defined data that is passed in when the subscription is created, essentially serves the same purpose as the subscrData parameter in the callback() method

This implementation only uses the args parameter since the only thing that’s needed is the selected date. Once the selected date is obtained, it’s formatted into the standard string representation for dates that’s used by the widgets in this example. Lines 12 and 13 create a message with this string date value to be published onto the PageBus. Line 15 publishes the message onto the PageBus using its publish() function which takes two parameters: the first parameter is the topic to publish the message on, in this case "calendarEvents"; and the second parameter is the message to publish.

Integrating with the Flex Widget

The Flex widget, i.e. the ApptmtListWidget is subscribed to the "calendarEvents" topic on the PageBus so it receives the messages published by the CalendarWrapper. Since the CalendarWrapper, the messages that it publishes and the PageBus are all implemented in JavaScript while the AppmtListWidget is implemented in Flex, a mechanism to allow communication between JavaScript and Flex is needed. Flex provides two mechanisms for integration with JavaScript. One is the Flex Ajax Bridge and the other is the ExternalInterface--this example uses the ExternalInterface. For basic cases such as this one where all that is required is to expose a couple functions that pass simple parameters, the ExternalInterface is sufficient. For complex cases where there’s a rich set of functionality and complex object types that need to be exposed between Flex and JavaScript, the Flex Ajax Bridge is recommended.

To allow a Flex function, i.e. a function defined in ActionScript, to be called from JavaScript, the function must first be registered with the ExternalInterface class. Listing 6 below shows how the ApptmtListWidget’s callback() function is registered with the ExternalInterface so that it can be invoked by the PageBus. This is done as part of the initialization of the ApptmtListWidget.

Listing 6: Exposing the ApptmtListWidget’s callback() function via the ExternalInterface

Line 5 in listing 6 defines a function initApp() that is used to register the callback() function with the ExternalInterface. Line 2 registers initApp() with the Flex application’s creationComplete event so that it will get invoked after the application is created and initialized. In the implementation of initApp(), line 6 first checks to make sure that the ExternalInterface is available, i.e. whether the current Flash Player is in a container that offers an external interface--a requirement for using the ExternalInterface class. If it is available, then line 7 registers the function with the ExternalInterface by invoking its addCallback() function. It is just a coincidence that in this example the function that needs to be registered also happens to be named "callback". The ExternalInterface’s addCallback() function takes two parameters. The first, a function name as a string, is the name by which the function will be exposed to JavaScript. The second parameter is the actual ActionScript function that will be executed when the container calls the defined function name. They can be different, e.g. the ActionScript callback() function can be exposed as "flexCallback" to JavaScript.

Listing 7 shows the source for the callback() function. The parameters are the same as for the callback() function for the CalendarWrapper. When the PageBus invokes the function, the ExternalInterface converts the parameters from JavaScript into ActionScript Objects along with their corresponding properties. The implementation retrieves the date out of the message (line 6) and uses it in a filter function to filter the datagrid to show only those appointments on that date (lines 7 and 8).

Listing 7: callback() function for the Flex-based ApptmtListWidget

Listing 8 show’s the ApptmtListWidget’s publishToPageBus() function that responds to the datagrid’s itemClick event to publish a message to the PageBus that contains the date of the selected appointment from the datagrid.

Listing 8: ApptmtListWidget’s publishToPageBus() function

Line 18 shows how the function is registered with the itemClick event similar to how the initApp() function is registered with the Flex application’s creationComplete event in listing 6. In the function’s implementation in lines 5 to 10, a PubSubMsg is created along with a MsgParam that consists of a name-value pair with the name set to "date" and the value set to the date of the selected appointment from the datagrid. After checking to make sure that the ExternalInterface is available, line 12 uses the ExternalInterface’s call() function to invoke the PageBus’ publish() function to publish the message. The first parameter of the call() function is the name of the JavaScript function to invoke. The parameters following that are the parameters that are expected by the PageBus’ publish() function--the topic on which to publish the message and the message itself. This completes the example so that the Flex-based ApptmtListWidget can send messages to and respond to messages from the Javascript-based CalendarWidget via the PageBus and vice-versa.

Conclusion

Web-based user interfaces that are composed of multiple widgets are now the norm. This article has demonstrated how to provide a more cohesive experience to the user by enabling the widgets embedded on a page to interact with each other through a publish/subscribe messaging architecture. This is a natural fit because most user interfaces already utilize some type of event-driven mechanism to support user interactions. Extending this to incorporate the use of a messaging bus located within the page will enable a more loosely-coupled architecture in which widgets can be added and removed more easily and perhaps even more dynamically. Although the example in this article was relatively simple, the approach and architecture described can serve as a foundation for building more complex user interfaces with multiple embedded widgets talking to each other. In a real application, it will be important to have standard message structures and a standard set of subscription topics to really achieve the loose coupling that is promised by the publish/subscribe architecture.

Resources

More Stories By Tieu Luu

Tieu Luu works at SuprTEK where he helps the U.S. government create and implement strategies and architectures that apply innovative technologies and approaches in IT. You can read more of Tieu’s writing at his blog at http://tieuluu.com/blog.