Tom Sugden: July 2009 Archives

« January 2008 | Main | August 2009 »

July 18, 2009

A Declarative Approach to Flex Popups

Yaniv De Ridder and myself have developed a small Flex library for opening and closing popups. Instead of using the PopUpManager directly and writing script-block logic to manage their creation and removal, a pair of simple MXML tags are available for declaring within view components. Here's the "Hello World" of declarative popups:

<popup:PopUpWrapper open="{model.openPopUp}">
   <mx:Label text="Hello World"/>
</popup:PopUpWrapper>

The PopUpWrapper tag is a non-visual component that manages opening and closing the popup. When its open property is set to true, a popup is opened containing the component wrapped by the tag; in this case a Label. When the open property is set back to false, the popup closes again. Alternatively, the component may dispatch an Event.CLOSE event, which will be handled by the PopUpWrapper itself.

This approach helps to keep MXML views components clean and free from ActionScript logic, whilst removing duplicated code wherever the PopUpManager is needed. The opening and closure can be controlled conveniently through bindings, as above, which plays nicely with presentation models. There are also simple ways to control the life-cycle of the popup and to apply special behaviors, such as effects that play while it opens and closes.

The remainder of this post covers the two components available -- PopUpWrapper and PopUpFactory -- explaining the differences between them. The library, source code, unit tests and a sample application are available for download here:

Using Declarative Popups

There are two components with slightly different capabilities:

  • PopUpWrapper - this is the simplest tag. It defers the creation of the popup view until the first time the popup is opened, then reuses the same view every subsequent time. There is no mechanism for releasing that view to be garbage collected.
  • PopUpFactory - this is the more flexible but less simple tag. It also defers creation of the popup view, but gives control over whether or not the view should be reused when the popup is next opened. If reuse is disabled, the view is made eligible for garbage collection upon closure.

Some examples of each are now provided.

The PopUpWrapper Component

Here's a more detailed version of the "Hello World", this time showing all of the properties and events available:

<popup:PopUpWrapper
    open="{model.openPopUp}"
    center="true"
    modal="false"
    childList="{PopUpManagerChildList.POPUP}"
    opening="openingHandler()"
    opened="openedHandler()"
    closing="closingHandler()"
    closed="closedHandler()">
    <mx:Label text="Hello World!"/>
<popup:PopUpWrapper>

The properties provide the same control over the popup as the PopUpManager. The events are dispatched at various points during the life-cycle of a popup:

  • PopUpEvent.OPENING is dispatched after the popup view has been created but just before it has actually been added to the display list.
  • PopUpEvent.OPENED is dispatched just after the popup view has been added to the display list.
  • PopUpEvent.CLOSING is dispatched just before the popup view is removed from the display list.
  • PopUpEvent.CLOSED is dispatched just after the popup view has been removed from the display list.

Here's another example where the popup is opened or closed using buttons instead of binding. The view used for the actual popup is a custom component.

<popup:PopUpWrapper id="popup">
    <mypopup:MyPopupView/>
</popup:PopUpWrapper>

<mx:Button label="Open" click="popup.open = true"/>
<mx:Button label="Close" click="popup.open = false"/>

The popup view itself can also instruct the closure of the popup by dispatching an Event.CLOSE event. For example:

<mypopup:VBox ... >
    <mx:Label text="My Popup!"/>
    <Button 
        label="Close" 
        click="dispatchEvent(new Event(Event.CLOSE))"/>
</mypopup:VBox>    

The PopUpFactory Component

The PopUpFactory tag uses a different mechanism for specifying the popup view. It is in fact the same approach that list-based controls use for their item renderers. The factory property can either be set to the class name for the popup view, or else an in-line component can be declared.

Here is a simples use case, specifying the popup view class name:

<popup:PopUpFactory 
    open="{model.openPopUp}"
    factory="my.package.MyPopupView"
    reuse="false"/>

Notice that the PopUpFactory includes a reuse property. This gives control over whether or not the popup view is reused, in contrast to the PopUpWrapper tag which always reuses the view and never makes it eligible for garbage collection.

When working with the PopUpManager directly it is common to make popups eligible for garbage collection once they have been removed. However there are good use cases for both approaches. If an application shows a large popup at start-up and then never again, it is wasteful of resources to keep it in memory. Alternatively, if a popup is repeatedly shown and hidden, such as a context menu, it is more efficient to reuses a single instance. As with all Flex development, the Flex Profiler should be used to ensure popup views are successfully garbage collected, since they can be a source of memory leaks.

Here is another example where the popup view is declared as an in-line component, in a similar manner to an item renderer on a list-based control.

<popup:PopUpFactory 
    open="{model.openPopUp}"
    reuse="false">
    <mx:Component>
        <mypopup:MyPopupView
            title="{outerDocument.model.popUpTitle}"/>
    </mx:Component>
</popup:PopUpFactory>

The PopUpFactory tag also provides the same properties as the PopUpWrapper tag, since they both inherit from a common base class.

Popup Behaviors

When we first introduced a declarative approach for popups to our projects, we noticed developers customizing the classes with more and more specific behavior. In one situation, a popup needed to play a transition while opening; in another the popup had to remain centered whenever its contents resized. Soon the popup tags became bloated with code for handling all of these special requirements, each applicable only to one or two popups.

The IPopUpBehavior interface was introduced to extract these specialities into their own classes. In this way, a set of behaviors can be created, independent of one another, and the appropriate behaviors can be applied for a given situation. This design creates a nice extensibility point, allowing developers to write their own behaviors without needing to customize the declarative tags at all.

Here is an example of a popup with a number of behaviors applied.

<popup:PopUpFactory
    open="{model.showPopUp}"
    factory="my.package.MyPopUpView">
    <popup:behaviors>
        <mx:Array>
            <popup:ZoomAndFadePopUpBehavior/>
            <popup:KeepCenteredPopUpBehavior/>
        </mx:Array>
    <popup:behaviors>
</popup:PopUpFactory>

In this case, two custom popup behaviors are being applied. The first plays a zoom and fade effect upon opening and closure, while the second ensures that the popup remains centered whenever its contents is resized. The declarative approach makes it simple to configure these behaviors.

To write your own popup behavior, just implement the IPopUpBehavior interface:

public interface IPopUpBehavior
{
    function apply(opener:PopUpBase):void;
}

A reference to the popup component is passed through the apply() method to each behavior. The behavior can then attach event handlers in order to control the behavior of the actual popup while it is opened or closed. Some examples are provided in the sample project (download above). A behavior can also suspend and resume closure by calling the suspendClosure() and resumeClosure() methods of the PopUpEvent class. This allows something asynchronous, such as the playing of an effect, to take place during closure, before the popup is actually removed from the display list.

How Does It Work?

The design for the popup tags is relatively simple. There's a common base class -- PopUpBase -- that is extended by the two tags: PopUpWrapper and PopUpFactory. The base class contains the common properties -- open, center, modal -- and also holds an array of behaviors, applying them during opening and closure. The two concrete tags implement different creation and destruction policies using the standard mechanisms for deferred instantiation and object factories: IDeferredInstance and IFactory. The Flex compiler takes care of the rest, converting the declared popup view into an appropriately configured DeferredInstanceFromFunction/Class or ClassFactory object. See the Flex Language Reference for more details.

Conclusion

We've been using versions of these tags on our projects for several years now. They help us to separate concerns, keeping our views as simple declarations of components, free from script block logic that would make them harder to understand and more difficult to unit test.

It's worth noting that Flex 4 includes its own declarative tag -- PopUpAnchor -- which also takes advantage of MXML and deferred creation, but it is designed for drop-down menu scenarios. There are some similarities, but the PopUpWrapper and PopUpFactory components are designed for more general popup handling. Perhaps a future version of the SDK will include declarative tags that make it simple to manage the life-cycle of popups and apply different behaviors, but in the meantime we hope you enjoy using these components.

Posted by tsugden at 10:07 PM | Comments (5)

July 6, 2009

The Trend Towards Inversion-of-Control Frameworks in Flex

Last year I wrote three chapters about the Cairngorm MVC framework for a new Flex book, Professional Flex 3, published by Wrox Press. Cairngorm has been widely adopted on enterprise projects, so it was important to explain its workings and cover the best practices and common pitfalls. If you’re a developer who moves between clients and projects, this is important knowledge to have. However, with more time and pages, I’d have liked to devote similar attention to the other Flex frameworks now available.

Over the last few years, the Flex framework space has become well served with alternatives to Cairngorm, each offering their own take on the challenges of RIA development. While Cairngorm is rooted in design patterns from the Java enterprise world, some of the newer framework creators have taken a fresh approach, reflecting on the language features and special capabilities of ActionScript and the Flex SDK, then building from the ground up, taking advantage of features like event bubbling, declarative MXML and metadata.

Inversion-of-Control

In these new frameworks, there has been a trend towards the inversion-of-control (IoC) or dependency-injection pattern. There is a good reason for this, since this approach decouples the objects of an application, so they can change independently of one another and be tested in isolation. Applying IoC can make it easier to restructure a user interface, substitute the service integration layer, and refactor the models containing the logic and state of your application, amongst other benefits.

For those new to the concept, IoC means passing (or injecting) the dependencies for a class into that class, instead of creating the dependent objects within. The behavior of a class can then be controlled by passing in different types of objects; an inversion of the traditional approach. When interfaces are extracted from these dependencies, the technique becomes more powerful, because the class is decoupled from any concrete classes. The interfaces can be implemented in different ways to achieve different outcomes and can be substituted for testing purposes.

An example of this design is found in the Tree component of the Flex SDK. The Tree uses the ITreeDataDescriptor interface to determine whether the items in its data provider are branch or leaf nodes.

public interface ITreeDataDescriptor
{
    ...
    isBranch(node:Object, model:Object = null):Boolean
    ...
}

This interface decouples the Tree from the actual type of objects inside the data provider. By substituting different implementations of ITreeDataDescriptor, it can be made to render all kinds of objects in tree form. This inversion of control makes the Tree a more flexible component than it would be if a single data descriptor of fixed type was created internally.

Inversion-of-Control Frameworks in Flex

A number of IoC-based frameworks have emerged for Flex to support the development of loosely-coupled applications. They typically provide a way to configure the main objects used by the application (models, services, etc.) and to specify the dependencies between them. These frameworks are sometimes known as IoC containers, where the container is the part of the framework responsible for instantiating objects and setting-up (or wiring) the dependencies. In some cases, the container assumes further responsibilities, such as managing the life-cycle of the objects (calling initialization and disposal methods) or routing messages from one object to another.

To give a potted history, two of the earlier arrivals were Prana and Parsley, both of which were inspired by the popular Spring framework from the Java world. They used XML files for configuration and singletons for extracting pre-configured objects from the containers into Flex views. The generation that followed, including Swiz and the lesser-known Flicc, were lighter weight and took more advantage of features of Flex. Swiz took the most radically simple approach, defining objects (or beans) directly in MXML and injecting them automatically into views wherever a special metadata tag was placed, while Flicc provided a novel set of MXML tags for object definition, event handling and view injection.

Since then all of these frameworks have been evolving. Prana has become Spring ActionScript, an extension of the official Spring Platform, while Parsley picked up some tricks from Swiz and Flicc and broadened its feature set to include messaging, module integration and extensibility points. Swiz too has gained new capabilities while remaining distinctly lightweight, like prototype object instantiation (a.k.a. non-singletons) and more flexible event mediation with bubbling events and metadata. A more detailed comparison of these frameworks together with sample applications is due to be published soon on Adobe Devnet in an article written by my colleague, Ed Eustace (link will be added after publication).

Conclusion

The frameworks space is in good shape for Flex. Established MVC frameworks like Cairngorm and Pure MVC, that prescribe certain design patterns and structures, have been complemented by a slew of new frameworks1 centered around inversion-of-control. When applied correctly, these enable developers to write more loosely-coupled software that can be changed and tested more easily.

However, inversion-of-control is a very general approach that can be applied in different ways. Questions remain about the best way to use it for building Flex applications. For advanced developers, the freedom afforded by these new frameworks is a benefit, since their experience enables them to make wise judgements about how to structure the layers of an application (view, model, control, integration, etc). For less experienced developers, however, the pattern alone provides little guidance and mistakes can easily be made. Since software project teams tend to consist of multiple developers with different levels of experience, it is important to establish guidelines around the use of an IoC framework for a team to deliver efficiently.

The variety of approaches and competition between frameworks is in the interests of Flex developers and end users. It’s giving us better solutions to the recurring problems of RIA development, allowing us to focus on what is special and distinct about our projects, rather than the basic plumbing. There are some opportunities for consolidation and there's no one-size-fits-all solution at present, so technical architects need to evaluate carefully and choose the best framework for a particular situation, given the technical considerations and also the makeup of the team.

Progress looks to be steady with the current selection of frameworks, but perhaps new capabilities are needed for Flash Player and the Flex SDK to enable the next major leap forward?

1 The Mate Flex Framework and probably others have been unfairly overlooked in this blog post. Although not positioned as an inversion-of-control container, Mate does provide a means of dependency injection together with a neat set of declarative MXML tags to structure and coordinate a Flex application.

Coming Up

In a future post, I’ll write in more detail about Parsley 2, the second version of the Parsley Application Framework. Parsley has arguably the richest feature set of the current generation of frameworks, yet it also has a simple elegance. The blog post will focus on two of the features that currently distinguish it: module integration and loosely-coupled messaging.

Posted by tsugden at 8:35 AM | Comments (0)