7 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.

7.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 = ...;
var builder:CompositeContextBuilder = new DefaultCompositeContextBuilder(viewRoot);
FlexContextBuilder.merge(BookStoreConfig, builder);
XmlContextBuilder.merge("logging.xml", builder);
builder.build();	

7.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:

<mx:Panel 
    xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:parsley="http://www.spicefactory.org/parsley"
    >
    
    <parsley:Configure/>
    
    <!-- ... -->
    
</mx: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:

<mx:Panel 
    xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:parsley="http://www.spicefactory.org/parsley"
    xmlns:view="myproject.view.*"
    >
    
    <view:MyPanelPM id="model"/>
    <parsley:Configure target="{model}" /> 
    
    <!-- ... -->
    
</mx: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.

7.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:

<mx:Panel 
    xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:parsley="http://www.spicefactory.org/parsley"
    xmlns:view="myproject.view.*"
    >
    
    <mx:Script>
        <![CDATA[
            import com.bookstore.model.MyPanelPM;
            
            public var model:MyPanelPM;
        ]]>
    </mx:Script>
    
    <parsley:FastInject property="model" type="{MyPanelPM}" /> 
    
    <!-- ... -->
    
</mx: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.

7.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.

Since this feature is quite new and not battle-tested yet, it has to be explicitly activated in the current version:

MXML

<parsley:ContextBuilder>
    <parsley:Settings autowireViews="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 7.6 MXML and XML Configuration for details.

But this is only the default mechanism. 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:

GlobalFactoryRegistry.instance.viewManager.autowireFilter = new MyAutowireFilter();

7.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:

<mx:Panel 
    xmlns:mx="http://www.adobe.com/2006/mxml"
    addedToStage="dispatchEvent(new Event('configureView', true));"
    >
    
    <mx: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;            
            }
        ]]>
    </mx:Script>
    
    <mx:text text="Current User: {user.name}"/>
    
    <!-- some more components ... -->
    
</mx: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.

7.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 7.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.

7.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. This affects the [Init] and [Destroy] metadata tags.

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 only get executed after the container has been destroyed with a call to Context.destroy() with the only exception of DynamicObjects which may be removed from the Context anytime. For a Flex Component that is dynamically wired 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.

Finally you can switch this default behaviour if the lifecycle of the component should not depend on the time it spends on the stage. You can control this with properties of the ViewManagerFactory, either in the GlobalFactoryRegistry or for a single Context with CompositeContextBuilder.factories. For Flex this can also be configured with the Settings tag:

<parsley:ContextBuilder>
    <parsley:Settings stageBoundLifecycle="false"/>
    <parsley:FlexConfig type="{MyConfig}"/>
</parsley:ContextBuilder>

With the setting above components will not be removed from the Context when they get removed from the stage. Instead such a component must now dispatch a custom "removeView" event when it wished to get unwired.

Finally this can now also be controlled for a single component:

<mx:Metadata>
    [RemovedEvent("removeView")]
</mx:Metadata>

7.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. This is quite easy:

Flex Popup

[Inject]
public var context:Context;

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

AIR Window

[Inject]
public var context:Context;

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

The removeViewRoot method of the ViewManager interface will not be called by application code directly in most cases, as the default implementation of the interface will automatically remove a view root when it is removed from the stage.

7.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.