Recently I have shifted my framework of preference from Cairngorm to Robotlegs. Cairngorm has been my choice of framework mainly because it has a large user-base support, so I can point developers to learn the framework on their own. Cairngorm has its limitation, and the one gives me the most heartburn is singleton model, which makes the code difficult to write unit test for.

Just a few hours of reading this awesome book ActionScript Developer’s guide to Robotlegs, I am sold. Within a short time, I am able to rewire up a Cairngorm-based applications into Robotleg’s fashion. Mediator in Robotlegs is an awesome designer. Without Mediator, my control’s container would have to know how to pass data and events back and forth. It gets cumbersome when there are layers of containers. Mediator mapping relies on listening the control’s ADDED_TO_STAGE event, then maps the control to the mediator class. However, the event listening is done on the context root of the Robotleg’s configuration. In a Flex application, typically the context root is set to the Application itself like the following

<s:Application 
			   xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:context="myapp.context.*">


	<fx:Declarations>
		<context:TabletApplicationContext contextView="{this}" />
	</fx:Declarations>

	...
</s:Application>

This works great in most cases because all the controls are usually added to the display tree of the Application, therefore the context is able to capture the control added event. However, when a control is introduced by PopupManager or Callout component, the control is not added to the application but application's parent, therefore the event is not captured by the context.

There has been discussions and samples to work around this problem. The solutions most discussed would use "Command" to perform the Popup instead of the component itself. This gives me a heartburn because Command is NOT supposed to interfere with UI implementation.

The Solution
Since the cause of the issue is the context is not able see the popup added simply because the popup is added outside the contextView, my solution is to find out what that "outsider" is. A quick dig around of PopupManager's implementation I found out it uses application's systemManager as the place to add the popup control. So I quickly implemented the solution below:

	<context:TabletApplicationContext contextView="{DisplayObjectContainer(this.systemManager)}" />

But it does not work as somehow the context is not instantiated. That's when the contextView is assigned, the systemManager is not added to the stage and the application is not ready. That's not too obvious, but we can always delay the contextView assignment until the application's creationComplete event is fired. So I modify my application as follows:

<s:Application creationComplete="initContext();" 
			   xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:context="myapp.context.*">


	<fx:Declarations>
		<context:TabletApplicationContext id="context" />
	</fx:Declarations>

	<fx:Script>
		<![CDATA[
			
			private function initContext():void
			{
				//Use systemManager as contextView so popups instantiated by PopupManager would be wired up properly
				var contextView:DisplayObjectContainer = systemManager as DisplayObjectContainer;
				context.contextView = contextView;
			}
			
			
		]]>
	</fx:Script>

	...
</s:Application>

Since then, the mediator is happily working with the popup control ever after!

Robotlegs 2
I read in the forum posts that the upcoming Robotlegs 2 is supposed to alleviated this pain. I will follow up with a post once I get RL2 and popup working with my new application.

About these ads