Handling deferred view component creation within the PureMVC framework
Jan 25th, 2008 by Tom Cornilliac
For my last AIR project and my current Flex project I've been using the PureMVC ActionScript framework. It's a solid framework and on the whole I'm enjoying working with it. When using PureMVC for Flex and AIR development one of the questions I see consistently is how to create mediators for deferred components. In other words, if the view of your app uses a view stack how do you handle creating mediators on the children of the viewstack that are not created yet?
PureMVC uses mediators for all communication from the model and controller to the view. Out of the box PureMVC normally creates the view mediators after creationComplete has fired and the model has been initialized. The reason for this is that mediators require a reference to a view component to work. In order to construct a mediator you first need an object or view component to pass to the constructor.
There are basically three different ways to handle this, two of which I don't recommend using.
1. Use dummy objects to initialize mediators
If you know that your view component is not on the stage yet you could still create it's mediator by passing a POASO (plain old actionscript object) to it's constructor. I have two problems with this solution; One you're unnecessarily using memory and two you have not treated the whole problem, you still need some type of event schema to reinitialize the mediator once the component has been created. I don't recommend using this solution.
2. Use creationPolicy="all" to avoid deferred views
By default components like ViewStack and TabNavigator on create their top level children at runtime. The remainder are created as the user navigates to them. You can use the creationPolicy="all" attribute to override this behavior. This causes the application to create all the view components at runtime. This is a terrible idea and I cannot recommend it. What you are in effect saying to your user is "I don't care about your memory or performance because I'm to lazy to code a decent solution" I know this is a bit dramatic but I hate when people use this attribute.
3. Use a custom Event subclass
The idea here is to create an event subclass that the application mediator or another mediator can listen for and use to create new mediators. This event class has an extra property which holds a reference to the newly created component, other mediators can use this reference to construct a mediator for the component. This approach not only keeps your memory footprint smaller but it allows you to easily create and destroy mediators. Here's an example of an Event subclass:
You create your own constant names based on the event type you want your mediator to listen for.
-
package com.tomcornilliac.myproject.events
-
{
-
import flash.events.Event;
-
-
public class ComponentCreationEvent extends Event
-
{
-
// Publically accessible properties
-
public var component:Object;
-
-
//Constants used for routing withing PureMVC mediators
-
public static const SOME_EVENT:String = "gatewayCreated";
-
public static const SOME_OTHER_EVENT:String = "gatewayStackCreated";
-
-
public function ComponentCreationEvent(type:String, component:Object, bubbles:Boolean=false, cancelable:Boolean=false)
-
{
-
super(type, bubbles, cancelable);
-
//Probably should have a getter/setter for this property
-
this.component = component;
-
}
-
-
//You must override the clone method
-
override public function clone():Event
-
{
-
return new ComponentCreationEvent(type, component, bubbles, cancelable);
-
}
-
}
-
}
An instance of this event is dispatched from your view component with a reference to its self. Note that "bubbles" is set to true, this is required for the event to make it up the chain to whatever mediator is listening.
-
<?xml version="1.0" encoding="utf-8"?>
-
-
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
-
width="100%" height="100%" creationComplete="init(event)">
-
-
<mx:Metadata>
-
[Event(name="gatewayCreated", type="org.deschutes.grizzly.events.ComponentCreationEvent")]
-
</mx:Metadata>
-
-
<mx:Script>
-
<![CDATA[
-
import com.tomcornilliac.myproject.events.ComponentCreationEvent;
-
-
private function init(e:Event):void
-
{
-
dispatchEvent(new ComponentCreationEvent(ComponentCreationEvent.GATEWAY_CREATED, this, true));
-
}
-
]]>
-
</mx:Script>
-
-
</mx:Canvas>
The listening mediator (in this case the ApplictionMediator) will respond to the event and create and register a new mediator for the newly created view component. Now it's important to note that while I'm using the ApplicationMediator in this example, you can use any mediator you want, so long as the event will bubble-up to it.
-
package com.tomcornilliac.myproject.view
-
{
-
import com.tomcornilliac.myproject.events.ComponentCreationEvent;
-
import com.tomcornilliac.myproject.view.components.SomeComponent;
-
import org.puremvc.interfaces.IMediator;
-
import org.puremvc.interfaces.INotification;
-
import org.puremvc.patterns.mediator.Mediator;
-
-
public class ApplicationMediator extends Mediator implements IMediator
-
{
-
//Canonical name of the mediator
-
public static const NAME:String = 'ApplicationMediator';
-
-
//Constructor
-
public function ApplicationMediator(viewComponent:Object)
-
{
-
super(viewComponent);
-
-
/**
-
* Setup event listeners for components created dynamically
-
*/
-
app.addEventListener(ComponentCreationEvent.SOME_EVENT, createNewMediator);
-
}
-
-
private function createNewMediator(e:ComponentCreationEvent):void
-
{
-
//Create the new Mediator and initialize it with the component from our event
-
var mediator:SomeComponentMediator = new SomeComponentMediator(e.component);
-
//Register the mediator with the Application Facade
-
facade.registerMediator(mediator)
-
//That's it now your view component is hooked-up and ready to handle I/O
-
}
-
-
....other methods
This technique is not only good for component creation but you could just as easily create a custom ComponentDestructionEvent and use it to remove Mediators off the stack when no longer needed.
If you would like more information about the PureMVC framework I encourage you to visit the PureMVC web site. The documentation is excellent and there's even a training course to help you on your way.
Thanks for this blog post.. I’ve been using PureMVC for the past 2 weeks and love it. But I have a question. Why did you decide to use a custom event class. Couldn’t you also just use notifications and let a Command handle all this work? I’m just curious because I need this type of functionality and I was gonna work with commands …… BUT.. if doing it this way is better (for memory ect) I’ll use custom events also.
Hi Mike - Sending a PureMVC notification from my view component to my view mediator wouldn’t be a good idea at all. Doing so would require that my view component have knowledge of the framework which in turn would introduce coupling. Coupling in the view defeats the purpose of the view mediator. Using the Event class allows me to keep my component in the dark about it’s implementation, thereby promoting reuse.
Now I would definitely use a Command/Notification setup to perform work on the Model from the View or maybe even within the View if the work was complicated enough. Hope this helps.
That helps a lot and makes sense. Thanks!!
One more question Tom (if you don’t mind)…
What would be the best way to remove a mediator using the ComponentDestructionEvent… I’m trying to figure out how to get the ApplicationMediator to figure out what component just went out of view… and what mediator to take off.
I am really glad that you are enjoying the framework. And this is an awesome example of a complex problem. But seeing this reminds me of what I thought when I saw pureMVC for the first time.
“And people thought Cairngorm was complicated”
I think its great pureMVC jumps the gaps between Flex and flash and the server side stuff. But I honestly cant imagine implementing all this stuff in a large application.
I am excited to hear more of your reviews of it. I want to know if when you are done with a big project (something that warrants a heavy duty framework) if you feel all this stuff was worth while.
Thanks for the post!
@Mike - There are at least a couple different ways that I can think of to trigger mediator removal when it’s DisplayObject (component) is removed from the display list.
1. You could use a custom event subclass dispatched by the “removed” or “removedFromStage” event. The process here is almost exactly the same as in my post with the exception that you would call “facade.removeMediator(componentName)” from the listening mediator instead of creating a new mediator.
2. When you initially create the mediator, in the constructor method, add an event listener for the “removed” or “removedFromStage” event. Depending on which event you use your mediator will be notified just before it’s about to be removed from the display list. You can then have your mediator destroy itself.
I’d probably use method #2 most of the time. Method #1 is a bit overkill for most apps. Besides that method #2 requires your view component to dispatch one less event, which is always good.
@Simeon - Thanks for stopping by man! I agree in that in PureMVC managing mediators can get a little bit complicated in larger apps, it’s almost like the mediators are dancing with the display list and trying not to step on each other’s toes (ok that was a lame analogy). When I first started with PureMVC this was not an issue as my app was on the smallish side, now that I’m working on a larger app I find myself thinking about this a lot. Here’s couple of vague ideas on solving this problem.
I wonder if the Factory Pattern could apply here. Could I build a mediator factory (or would that be an Abstract Factory). The idea obviously is that the factory would handle all the dirty work.
Or maybe some sort of dependency injection where display objects are injected into mediators at runtime so when display objects are added to the display list the mediator is already bound to them.
Food for thought anyway.
Hi Tom,
I generally defer the creation of the Mediator until its view component exists. The way you’ve done it here illustrates what what the ApplicationMediator’s job usually ends up being.
You can send a bubbling Event (no need for a custom event really) from any depth within your view hierarchy in the creationComplete for your new view component, and then grab it at the top with the ApplicationMediator.
The event will have a reference to the component (event.target), which you can inspect any property of (such as id) in a switch statement, creating the appropriate Mediator depending upon the component and register it.
-=Cliff>
Hi Cliff - You make a good point, I haven’t given much thought to interrogating the view component to determine which mediator to use. I’m curious, which property are you using for the Switch? Do you use a public static const like name?
Thanks for stopping by and adding!
hi!
very interesting…
but doesn’t it add another problem? what if the command is passing data to the -still not created- mediator?
ie: view in a viewStack has a datagrid which on selection dispatches a notification (via its mediator) carrying dg selected data. Notification handles the viewStack change, but cannot pass the selected data to the new view as its mediator is still not created.
How do you deal with that? where would you store the transferred data while waiting on view creationComplete().
Sorry for the poor English.. hope it is understandable
hi! it’s me again. Just to inform you that i posted the same question on puremvc forum (”architecture” / topic: viewstack, deferred mediator creation).