8 Dynamic View Wiring

So far all objects that "live" inside a Parsley Context have been defined with either MXML, XML or ActionScript as detailed in 3 Configuration and Initialization. For Flash Applications these mechanisms will usually be sufficient as it is very likely that you are able to conveniently define all managed objects in XML or ActionScript - including view elements. For Flex Applications this approach is not ideal since you'll prefer to declare your components in your MXML files within the component hierarchy and not in a separate Parsley Context MXML configuration class. So we'll need a different mechanism to connect these components defined within your MXML view definitions to objects declared with Parsley configuration files. The solution Parsley offers for this use case will be described in this chapter.

8.1 Initializing View Wiring Support

For view wiring each Context needs one or more so called view roots, which is a DisplayObject where the framework listens for bubbling events from components that wish to get wired to the Context. The mechanism is different whether you are using MXML tags to initialize the Context or whether you do that programmatically.

Context intialization with MXML tags

In Flex applications you can use the ContextBuilder tags introduced with version 2.2. Those will automatically use the document object they are placed upon as the view root:

<parsley:ContextBuilder config="{BookStoreConfig}"/>

In rare cases where this is not desired you may alternatively specify the view root explicitly:

<parsley:ContextBuilder config="{BookStoreConfig}" viewRoot="{someOtherDisplayObject}"/>

Programmatic Context intialization

If you initialize the framework programmatically the view root has to be specified explicitly:

XML configuration

var viewRoot:DisplayObject = ...;
XmlContextBuilder.build("bookStoreConfig.xml", viewRoot);

Multiple configuration mechanisms

var viewRoot:DisplayObject = ...;

ContextBuilder.newSetup()
    .viewRoot(viewRoot)
    .newBuilder()
        .config(FlexConfig.forClass(BookStoreConfig))
        .config(XmlConfig.forFile("logging.xml"))
        .build();

8.2 Explicit Component Wiring

In case you want to specify that a component should be wired to the Context directly within the component itself, you have two options to do so. First you can use the <Configure> tag:

<s:Panel 
    xmlns:fx="http://ns.adobe.com/mxml/2009"
    xmlns:s="library://ns.adobe.com/flex/spark"
    xmlns:parsley="http://www.spicefactory.org/parsley"
    >
    
    <parsley:Configure/>
    
    <!-- ... -->
    
</s:Panel> 

In Flex 4 this would need to go into the <fx:Declarations> tag. In the example above it would be the Panel (the component the tag is placed upon) that would be wired to the Context. Alternatively you can specify the object to be wired explicitly and it does not even have to be a component:

<s:Panel 
    xmlns:fx="http://ns.adobe.com/mxml/2009"
    xmlns:s="library://ns.adobe.com/flex/spark"
    xmlns:parsley="http://www.spicefactory.org/parsley"
    xmlns:view="myproject.view.*"
    >
    
    <view:MyPanelPM id="model"/>
    <parsley:Configure target="{model}" /> 
    
    <!-- ... -->
    
</s:Panel> 

In this example we declare a presentation model within the component and then instruct the framework to wire that model to the Context. In this case the component itself would not be managed by the container at all, so you cannot place any Parsley metadata tags into the component. This mode is primarily intended for performance optimizations. See the next section for a second option to avoid reflection on heavy component classes.

Wiring in Flash Applications

In Flash Applications the type of wiring as described in this chapter is often not needed. Without MXML serving as a nice means to define the view declaratively, you'd often declare view related objects directly in a Parsley XML or ActionScript configuration file. So there is no need to explicitly wire them.

In case you do not want to define the view in the Context and instead wish to use a mechanism where a view dynamically notifies the Context that it wishes to get wired, you could programmatically dispatch an event that is equivalent to the <Configure/> tag for Flex. For this to work you first have to specify the view root to be used to catch those bubbling events:

var viewRoot:DisplayObject = ...;
ActionScriptContextBuilder.build(MyConfig, viewRoot);

Then each DisplayObject below the specified view root can dispatch an Event when it was added to the stage to signal that it wishes to get wired:

public class MyView extends Sprite {

    function MyView () {
        addEventListener(Event.ADDED_TO_STAGE, configure);
    }
    
    private function configure () : void {
        dispatchEvent(new Event("configureView", true));
    }
    
    /* ... */
    
}

In earlier versions of the framework this type of wiring was also mentioned as an option for Flex Applications, but with the new features and enhancements in version 2.2 it is no longer recommended to use it. It would work in most cases, but all other mechanisms available for Flex described in this chapter (View Autowiring, <Configure> and <FastInject>) have the advantage to give you additional safety in that they contain a lot of synchronization logic that helps to avoid problems when a component initializes before the Context it belongs to is ready.

8.3 Component Wiring without Reflection

Parsley reflects on all managed objects and reflection on components currently is a very expensive operation due to their dozens of properties, methods and events and the fact that reflection is based on parsing XML. It may take up to 30 milliseconds for a single component. Parsley maintains an internal reflection cache so that each class is only processed once, but if you are using a high number of different component classes this may not help much. In a small application this effect may be negligible, but for larger applications it might indeed be a concern. This option allows to avoid a perceivable perfomance degradation in these cases.

To allow for performance optimizations the FastInject tag was introduced in Parsley 2.2:

<s:Panel 
    xmlns:fx="http://ns.adobe.com/mxml/2009"
    xmlns:s="library://ns.adobe.com/flex/spark"
    xmlns:parsley="http://www.spicefactory.org/parsley"
    xmlns:view="myproject.view.*"
    >
    
    <fx:Script>
        <![CDATA[
            import com.bookstore.model.MyPanelPM;
            
            public var model:MyPanelPM;
        ]]>
    </fx:Script>
    
    <parsley:FastInject property="model" type="{MyPanelPM}" /> 
    
    <!-- ... -->
    
</s:Panel> 

This is somewhat similar to the example with the <Configure> tag and its target attribute. Again it is the model that is wired to the Context. But the difference is that in the former example the model was created by the tag within the component and then the existing model instance got configured by the framework. In this example the container will create the model (it must be configured for the corresponding Context) and then inject it into the component. The major advantage of this approach is that the model could be shared between components which is not possible with the preceding example. Again use this mechanism only when performance is a concern and you want to avoid that the component itseld gets wired to the Context.

The <FastInject> tag also allows to define multiple injections with child tags:

<parsley:FastInject injectionComplete="init();">
    <parsley:Inject property="model" type="{MyPanelPM}"/> 
    <parsley:Inject property="status" type="{MyApplicationStatus}"/> 
</parsley:FastInject>

To combine them into a single parent tag if you want to perform some action when all injections have been processed. This would be cumbersome if you'd use separate tags since you'd need to manually check which injections have been already processed. In the example above there would be the guarantee that the init method will only be invoked when all injections have been performed.

There are similar events for the <FastInject> tag like creationComplete or addedToStage which differ from the original component events that those are only dispatched when the corresponding component event was dispatched and the injections have been performed, facilitating to find the right moment to initialize the component.

8.4 Automatic Component Wiring

Since version 2.2 you can also rely on an automatic variant of view wiring and avoid the need to add framework specific configuration to individual component classes. This mode was not trivial to implement as Parsley is a multi-Context environment and for automatic wiring there is the additional difficulty to know which Context each component should be wired to. This is now done based on their placement in the view hierarchy. A component is wired to the nearest Context in the hierarchy above the component.

Autowiring has to be explicitly activated:

MXML

<parsley:ContextBuilder>
    <parsley:ViewSettings autowireComponents="true"/>
    <parsley:FlexConfig type="{MyConfig}"/>
</parsley:ContextBuilder>

ActionScript

GlobalFactoryRegistry.instance.viewManager.autowireFilter.enabled = true;

The line above must be executed before the first Context gets created. As you might expect the autowireFilter is pluggable like almost everything in Parsley. So you can implement your own selection logic, telling the framework which components should be wired. The default implementation simply wires all components which are listed in the Context configuration (in MXML or XML) with the new <View> tag. This way configuration is really centralized and you can see which objects and views are managed by the container by examining a single configuration class. A sample configuration snippet might look like this:

<View type="{MainView}"/>
<Object type="{MainViewPM}"/>

<View type="{StatusBar}"/>
<Object type="{StatusBarPM}"/>

<View type="{ImageTileList}"/>
<Object type="{ImageTileListPM}"/>

<View type="{ImagePreviewWindow}"/>
<Object type="{ImagePreviewWindowPM}"/> 

Here we simply list the views and their corresponding presentation models as pairs. Only the views listed here will get wired to the Context. In this example we are only using empty View tags, but you might also nest tags within them the same way like with Object tags. See 8.6 MXML and XML Configuration for details.

As you can see in the example above we are using the <View> tags to declare custom views that are specific to the application. The built-in components from the Flex SDK that live in the spark.* or mx.* packages or the ones from the Flash Player API (flash.* packages) are prefiltered to improve performance, so they cannot be configured with <View> tags if you use the default setup. It's usually not a good idea to configure them like this as you have lots of them and the matching to their configuration can become challenging. So normally you'd wire your custom panels, windows, popups, tabs and boxes and pass things down to the components that those views contain.

But this is only the default mechanism. If you need more finegrained control over which components to wire you can simply implement your own autowire filter and for example use a mechanism that selects components based on the package they belong to like some other frameworks handle this. The easiest way is to simply extend DefaultViewAutowireFilter and overwrite the filter method:

public class MyAutowireFilter extends DefaultViewAutowireFilter {

    public override function filter (view:DisplayObject) : ViewAutowireMode {
        if (... someCondition ...) {
            return ViewAutowireMode.ALWAYS;
        }
        else {
            return ViewAutowireMode.NEVER;
        }
    }

}

And finally install that filter like this:

<parsley:ContextBuilder>
    <parsley:ViewSettings 
        autowireComponents="true" 
        autowireFilter="{new MyAutowireFilter()}"
    />
    <parsley:FlexConfig type="{MyConfig}"/>
</parsley:ContextBuilder>

8.5 Metadata Configuration

In many cases metadata is sufficient and the most convenient configuration mechanism. The metadata tags have the same effect on a component like on a regular object created by the container:

<s:Panel 
    xmlns:fx="http://ns.adobe.com/mxml/2009"
    xmlns:s="library://ns.adobe.com/flex/spark"
    xmlns:parsley="http://www.spicefactory.org/parsley"
    addedToStage="dispatchEvent(new Event('configureView', true));"
    >
    
    <fx:Script>
        <![CDATA[
            import com.bookstore.events.*;
            import com.bookstore.model.*;
            
            [Bindable]
            private var user:User;
            
            [Inject]
            public var model:LoginPanelPM;    
            
            [MessageHandler]
            public function handleLogin (event:LoginEvent) : void {
                this.user = event.user;            
            }
        ]]>
    </fx:Script>
    
    <s:Label text="Current User: {user.name}"/>
    
    <!-- some more components ... -->
    
</s:Panel> 

Many developers prefer to avoid to add too much logic to their components. In these cases the only Parsley tag used in a component is often the [Inject] tag on the presentation model which encapsulates all the logic and data required by that component.

8.6 MXML and XML Configuration

Since version 2.2 you can alternatively (or additionally) configure views in MXML or XML. This comes in handy in two scenarios: First you may want to use the same component class in different scenarios and thus cannot use metadata configuration as those tags can only be applied on the class level not on the instance level. Second you may want to use this configuration mechanism to specify the components eligible for autowiring as demonstrated in 8.4 Automatic Component Wiring.

This feature was inspired by the configuration mechanism used in Flicc. The container applies the configuration to the view when it is wired to the Context. The configuration to apply to a wired component will be matched either by id or by type:

<View id="myPanel" type="{MyPanel}">
    <Property name="url" value="http://www.somewhere.com/"/>
    <MessageHandler method="execute"/>
</View>

It is basically the same syntax like for regular object configurations. But the use of the View tag instructs Parsley not to attempt to create instances of that class but instead wait until any matching component gets dynamically wired to the Context and then applies the configuration.

Parsley will first attempt to match by id. This is either the name of the Flex component or alternatively an id specified explicitly in the Configure tag:

<parsley:Configure configId="someId"/>

If no match for the id is available then the framework will attempt to match by type, throwing an Error if it runs into ambiguous definitions. If no matching type is found Parsley will fall back to the old mechanism to only process metadata for that instance.

8.7 Component Lifecycle

Due to the fact that Flex Components are connected to the IOC Container "on the fly" the lifecycle for Components is different than for objects defined directly in the container. Parsley offers several configuration options to control that lifecycle.

Controlling the Lifecycle of Components

The default behavior is that a component gets removed from the Context automatically when it gets removed from the stage. But this can be changed through an attribute of the ViewSettings tag:

<parsley:ContextBuilder>
    <parsley:ViewSettings autoremoveComponents="false"/>
    <parsley:FlexConfig type="{MyConfig}"/>
</parsley:ContextBuilder>

With the setting above a component must now dispatch a custom "removeView" event when it wishes to get unwired. Note that this option does not have any influence when you use <FastInject> as in this case the target component is not managed at all and will just receive a one-off injection.

Controlling the Lifecycle of View Roots

Closely related is the lifecycle of the so-called view roots. If you use the <Configure> tag for example, this tag sends a bubbling event to signal that it wishes to get wired to the nearest Context. Other view wiring options like autowiring work in a similar way. A view root is the DisplayObject that catches these bubbling events. When you use the <ContextBuilder> tag, the component that holds this tag automatically becomes such a view root. The next section on popups and AIR windows shows that there are some scenarios where you need to add additional view roots to the Context.

For these view roots the default behavior is to control their lifecycle in a similar way like for wired components. It means that a view root gets removed from the Context when it gets removed from the stage, so it no longer listens for bubbling events. On top of that the associated Conetxt gets destroyed when the last view root gets removed. This is very useful in cases where a Context is declared in a popup for example. You don't need to destroy it explicitly, this happens automatically when the user closes the popup. In case you want to prevent this there is another flag specifically for view roots:

<parsley:ContextBuilder>
    <parsley:ViewSettings autoremoveViewRoots="false"/>
    <parsley:FlexConfig type="{MyConfig}"/>
</parsley:ContextBuilder>

Settings for Individual Components

The two preceding sections showed switches for globally controlling the lifecycle of components and view roots. But there is also a way to change the settings for a single component:

<fx:Metadata>
    [Autoremove("false")]
</fx:Metadata>

Beware of Memory Leaks

If you use one of the configuration options shown above that switch off the automatic lifecycle management, you must be careful not to introduce memory leaks. A component (or any kind of object) cannot get garbage-collected as long as it is managed by Parsley. So if you turn off the lifecycle management, you have to make sure that one of the following conditions is met for all wired components in that Context:

Be careful with Component Initialization Logic

Many developers are tempted to initialize the Presentation Model of a managed component like this:

<s:Panel xmlns:fx="http://ns.adobe.com/mxml/2009" 
         xmlns:s="library://ns.adobe.com/flex/spark" 
         xmlns:parsley="http://www.spicefactory.org/parsley"
         width="400" height="300"
         addedToStage="init()" 
    >
    
    <fx:Script>
        <![CDATA[
            
            [Inject]
            public var pm:SomePM;
            
            private function init () : void {
                pm.init();
            }
            
        ]]>
    </fx:Script>

    <fx:Declarations>
        <parsley:Configure/>
    </fx:Declarations>
    
</s:Panel>

There are two major problems with this approach, and both often introduce bugs that surface late in the development lifecycle of an application and are often hard to narrow down:

This is how the right approach would look like.

Using <Configure> or Autowiring:

<s:Panel xmlns:fx="http://ns.adobe.com/mxml/2009" 
         xmlns:s="library://ns.adobe.com/flex/spark" 
         xmlns:parsley="http://www.spicefactory.org/parsley"
         width="400" height="300"
    >
    
    <fx:Script>
        <![CDATA[
            
            [Inject]
            public var pm:SomePM;
            
            [Init]
            public function init () : void {
                pm.init();
            }
            
        ]]>
    </fx:Script>

    <fx:Declarations>
        <parsley:Configure/>
    </fx:Declarations>
    
</s:Panel>

Using <FastInject>

<s:Panel xmlns:fx="http://ns.adobe.com/mxml/2009" 
         xmlns:s="library://ns.adobe.com/flex/spark" 
         xmlns:parsley="http://www.spicefactory.org/parsley"
         width="400" height="300"
         creationComplete="init()" 
    >
    
    <fx:Script>
        <![CDATA[
            
            public var pm:SomePM;
            
            private function init () : void {
                pm.init();
            }
            
        ]]>
    </fx:Script>

    <fx:Declarations>
        <parsley:FastInject 
            property="pm"
            type="{SomePM}"
            injectionComplete="init()"
        />
    </fx:Declarations>
</s:Panel>

In both examples shown above it is guaranteed that the injection has been performed when the init method gets executed.

When using <FastInject> you cannot use the [Init] or [Destroy] tags as the component is not managed in this case and metadata would not get processed. The behavior of these two metadata tags for wired views is explained in the following sections.

Methods annotated with [Init]

For an object declared directly within a Parsley configuration file these methods get executed after the container has instantiated and configured the object. For a Flex Component that is dynamically wired it will be invoked after the container caught the configuration event dispatched by the Component and after all injections have been processed.

Methods annotated with [Destroy]

For an object declared directly within a Parsley configuration file these methods get executed after the container has been destroyed with a call to Context.destroy() or (in case of DynamicObjects) when the object has been removed from the Context. For a Flex Component that is dynamically wired, per default the destroy method will be invoked after the Component has been removed from the stage. Of course that means that the same instance can have multiple invocations of its Init and Destroy methods in case it gets removed and re-added to the stage.

8.8 Flex Popups and Native AIR Windows

For Flex Popups and Native AIR Windows some extra step is required to tell the framework about them. This is because those are views which are disconnected from the view hierarchy below the main Application component. A Flex Popup usually sits somewhere right below the SystemManager. A Native AIR Window even comes with its own SystemManager. So you have to connect both manually to a Parsley ViewManager if you want to use view wiring in popups and windows. The following sections show you how to do this.

Flex Popup

The following code snippet assumes that it is either part of a wired MXML component or another tpye of object managed by Parsley, so that the injection is actually performed:

[Inject]
public var context:Context;

private function showPopup () : void {
    var win:TitleWindow = new TitleWindow();
    // set properties
    context.viewManager.addViewRoot(win);
    PopUpManager.addPopUp(win, this);
}

Declarative Popups

Since version 2.3 you can now also specify popups declaratively within MXML components. This feature is based on the Cairngorm 3 popup library, so in order to use it you need to include the corresponding SWC. The Parsley download contains both the Flex 3 and Flex 4 version of that library in the libs folder.

The primary addition to the feature set supported by the Cairngorm tag is the transparent and automatic wiring to the Parsley Context. So in contrast to the examples shown above, you don't have to explicitly take care of connecting the Popup to the Parsley Context. All you have to do is using the <PopUp> tag in the Parsley MXML namespace instead of the <PopUpWrapper> tag in the Cairngorm namespace. Otherwise the Parsley variant supports the same set of attributes like the Cairngorm one:

[Bindable]
private var popupOpen:Boolean;

[...]

<parsley:PopUp open="{popupOpen}" center="true">
    <myNS:SomePopUp/>
</parsley:PopUp>

AIR Window

This is analogous to the wiring of a Flex Popup:

[Inject]
public var context:Context;

private function openWindow () : void {
    var win:Window = new Window();
    // set properties
    context.viewManager.addViewRoot(win);
    win.open();
}

Autoremoval of Contexts in AIR Windows

The default behavior for view roots in a Parsley application is to automatically get removed from the Context once the view is removed from the stage. For this to work Parsley must listen for stage events. This works reliably for Flex Popups without any additional plumbing, but unfortunately native AIR windows show a somewhat flaky behavior in that respect. So if you create a separate Context inside an AIR window and want it to get destroyed automatically when you close the window, some more work is required.

First switch off the autoremoval feature for the Context you create inside the AIR window:

<parsley:ContextBuilder config="...">
    <parsley:ViewSettings autremoveViewRoots="false"/>
</parsley:ContextBuilder>

This way Parsley does not listen to the stage events of the window, but instead for a custom "removeView" event. So you have to enhance the wiring of the AIR window shown in the previous section to dispatch this custom event:

[Inject]
public var context:Context;

private function openWindow () : void {
    var win:Window = new Window();
    // set properties
    context.viewManager.addViewRoot(win);
    win.addEventListener(Event.CLOSE, function (event:Event) : void {
        win.dispatchEvent(new Event("removeView"));
    });
    win.open();
}

In the current version there is no way to teach the framework to listen to the CLOSE Event directly, so the "translation" step shown above is required. A future version will very likely offer a shortcut for this scenario.

8.9 View Wiring in Modular Applications

The following diagram illustrates how view wiring works in a modular application and why it is important to manually connect Flex Popups and Native AIR Windows to a Context:

The Flex Components that contain a <Configure/> tag dispatch a bubbling event that gets caught by the next listener of a ViewManager in the hierarchy above that component. This way each component finds its matching Context, which is important because a component loaded into a module usually also needs access to the configuration of that module. If it would get wired to the root Context, it would only "see" the object in that Context.

For Context C in the diagram you'll see that two view roots were connected to the Context: One for the root component of the loaded module and one for a popup opened by the module. It becomes apparent now why this manual connection to the popup is necessary: if a component somewhere below the popup would dispatch a bubbling event it will end up in the SystemManager and never reach the ViewManager sitting on the root module component if we would not place a second listener on the popup itself.

Finally this mechanism also handles ApplicationDomains for us: The internal ModuleManager of the Parsley framework puts listeners on the root module component that catches bubbling events from ContextBuilders somewhere below and tells them the ApplicationDomain to use.

In some rare cases you may want to load a Flex Module which only contains a few MXML view classes which you want to be wired to the Context, but no controller, command, presentation or domain model. You might be tempted to skip Context creation in this case, but that would lead to problems as the components would be wired to the root Context then which reflects on the classes using a different ApplicationDomain. So you should still create a Context at the root component in the module, even if it is empty. This can simply be achieved through placing an empty <parsley:ContextBuilder/> tag into the root component.