23
Jan
10

RobotLegs Presentation Model implementation that access entire component events lifecycle

Last night, I was part of the RIARadio recording about RobotLegs. I have been supportive of the framework ever since I was introduced to it by Joel. We had an awesome time talking about Frameworks, OOP and RobotLegs. I raised a question to Shaun Smith and Joel Hooks about the presentation model vs Mediator and I pointed out that I haven’t seen a solution to the problem I presented on RobotLegs forums. See here. Joel challenged me to create my own implementation. I decided to accept the challenge and take a stab at it and see what I can come up with.

Problem

You cannot capture all the lifecycle events of a components ( such as PREINITIALIZE, INIT_PROGRESS, INITIALIZE & CREATION_COMPLETE) when using the mediator in Robotlegs. In addition I haven’t seen any implementation that uses the Presentation Model (PM) so you can do stuff before the component gets created or just drop an Presentation Model implementation you have done outside of the RobotLegs framework.

Under the hood

So why can’t we capture the component life cycle events in the Mediator?

The reason is that RobotLegs uses the contextView which is using the DisplayObjectContainer. The DisplayObjectContainer capture the event once the view is already added to the stage (see in 231 in MediatorMap.as)

The view gets added to the stage and than “onViewAdded” method being called and the mediator gets created. At this point it’s already too late, since most of the events to init the component have already been fired.

Meaning, even if you’ll change the mediatorBase code to the following:


		public function preRegister():void
		{
			if (flexAvailable && (viewComponent is UIComponentClass) && !viewComponent['initialized'])
			{
				IEventDispatcher(viewComponent).addEventListener('preinitialize', onCreationComplete, false, 0, true);
			}
			else
			{
				onRegister();
			}
		}

You cannot guarantee it will fire the pre-initialize event, and in fact when I have done some testing I found out that many times the if statement doesn’t get executed and it uses the onRegister directly.

Solution

So the options were either to modify RobotLegs entirely or try to create a more elegant solution that will use RobotLegs architecture while providing the ability to create presentation model in RobotLegs. Since I couldn’t think of a solution other than creating my own mapping system for the presentation model I started modifying RobotLegs framework, however as I was modifying the framework it hit me that there is a more elegant solution using the existing architecture.

Feel free to view and download (right click to ‘view source’) the full example from here:

screen-shot-2010-01-23-at-110559-am

Implementation

Let’s take a look at the example in detail. My entry point, main MXML application, calls the context:ApplicationContext just like any Robotlegs (MVCS) implementation. I have added component that gets attached to the view called mainView


<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/halo"
			   xmlns:context="com.elad.application.*"
			   minWidth="1024" minHeight="768"
			   xmlns:view="com.elad.application.view.*">

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

	<view:MainView id="mainView" width="100%" height="100%" />

</s:Application>

Using a presentation model will allows me to easy unit test my application better, since I can create test cases just for the logic and if needed test cases for the view. Additionally, I can replace my view easily and create different views for the same logic, which will help big time with Flex 4 new architecture and Flash Catalyst.

So using presentation model such as passive view I can than split my laundry to two piles, view and presenter:

View:
1. State is in the view
2. Transitions
3. View is passive and is not aware of the Presenter

Presenter:
1. Logic is in the Presenter.
2. Presenter observes view events
3. Presenter updates the view data.
4. Presenter ‘knows’ about the components in the view.
5. Presenter holds the data or point to a data class.

In fact, I have used the exact same example I created for Flash&Flex Magazine with very small modifications. Take a look at TweetListPresenter, which holds the responsibilities that I described:


package com.elad.application.view.presenter
{
	import com.elad.application.view.TweetListView;

	import flash.events.TimerEvent;
	import flash.utils.Timer;

	import mx.events.FlexEvent;

	import org.robotlegs.mvcs.Actor;

	import utils.twitter.TwitterHelper;
	import utils.twitter.events.TwitterHelperFailureEvent;
	import utils.twitter.events.TwitterHelperSuccessEvent;
	import utils.twitter.vo.TweetVO;

	public class TweetListPresenter extends Actor
	{
		/**
		 * Corresponding view
		 */
		private var _tweetListView:TweetListView;

		/**
		 * Method to go and retireve tweets every defined number of seconds
		 *
		 * @param seconds
		 *
		 */		

		public function get tweetListView():TweetListView
		{
			return _tweetListView;
		}

		public function set tweetListView(value:TweetListView):void
		{
			_tweetListView = value;
		}		

		/**
		 *  Twitter helper utility class instance
		 */
		public var twitterHelper:TwitterHelper;

		/**
		 *   Holds a timer so we will be able to update list
		 */
		public var timer:Timer;

		//--------------------------------------------------------------------------
		//
		//  Default Constructor
		//
		//--------------------------------------------------------------------------

		// called right after pre-init
		public function TweetListPresenter()
		{
			_tweetListView = new TweetListView();
			_tweetListView.width = 800;
			_tweetListView.height = 400;

			_tweetListView.addEventListener( FlexEvent.PREINITIALIZE, onPreinitialize );
			_tweetListView.addEventListener( FlexEvent.INITIALIZE, onInitialize );
			_tweetListView.addEventListener( FlexEvent.CREATION_COMPLETE, onComplete );

			twitterHelper = new TwitterHelper();
			twitterHelper.addEventListener( TwitterHelperSuccessEvent.RETRIEVE_TWEETS, onRetrieveTweets );
			twitterHelper.addEventListener( TwitterHelperFailureEvent.SERVICE_FAILURE, onFaultRequest );
		}

		public function retrieveTweetsEveryFewSeconds( seconds:int ):void
		{
			timer = new Timer( seconds*1000, 100000 );
			timer.addEventListener( TimerEvent.TIMER, onTimerHandler, false, 0, true );

			timer.start();
		}

		//--------------------------------------------------------------------------
		//
		//  Handlers
		//
		//--------------------------------------------------------------------------		

		private function onPreinitialize(event:FlexEvent):void
		{
			// implememt
			trace("onPreinitialize");
		}

		private function onInitialize(event:FlexEvent):void
		{
			// implememt
			trace("onInitialize");
		}

		private function onComplete(event:FlexEvent):void
		{
			trace("onComplete");
			retrieveTweetsEveryFewSeconds( 2 );
		}

		/**
		 * Method to handle a timer event
		 *
		 * @param event
		 *
		 */
		private function onTimerHandler( event:TimerEvent ):void
		{
			twitterHelper.retrieveTweetsBasedOnHashTag( "FlashAndTheCity" );
		}

		/**
		 * Handler for results
		 *
		 * @param event
		 *
		 */
		private function onRetrieveTweets( event:TwitterHelperSuccessEvent ):void
		{
			var dataProvider:Array = new Array();

			event.collection.forEach( function callback(item:TweetVO, index:int, vector:Vector.<TweetVO>):void {
				dataProvider.push( item );
			} );

			this._tweetListView.dataGrid.dataProvider = dataProvider;
		}

		/**
		 * Handler for fault
		 *
		 * @param event
		 *
		 */
		private function onFaultRequest( event:TwitterHelperFailureEvent ):void
		{
		}
	}
}

Couple of things to notice.

  • Actor - The presenter extends Actor just like any other RobotLegs actor
  • Component’s life cycle events - The default constructor creates the passive view and set the size as well as the component’s life cycle events

The view is passive and just holds the component (in our case a DataGrid):


<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
		 xmlns:s="library://ns.adobe.com/flex/spark"
		 xmlns:mx="library://ns.adobe.com/flex/halo"
		 width="400" height="300">

	<mx:DataGrid id="dataGrid" width="500" height="400" />

</s:Group> 

The next step is to create the mediator for the main view:


	public class MainViewMediator extends Mediator
	{
		[Inject]
		public var mainView:MainView;

		[Inject]
		public var tweetListPresenter:TweetListPresenter;			

		override public function onRegister():void
		{
			mainView.addElement( tweetListPresenter.tweetListView );
		}
	}

Notice that I am using the DI to inject the presenter just as we do to any Actor in RobotLegs:
[Inject] public var tweetListPresenter:TweetListPresenter;

And once the main view is registered I can than add the component to the display:
mainView.addElement( tweetListPresenter.tweetListView );

Lastly, I need to map everything up in the ApplicationContext class:


		override public function startup():void
		{
			//todo: add commands
            commandMap.mapEvent(ContextEvent.STARTUP, StartupCommand, ContextEvent, true);

			// todo: Add Model
            injector.mapSingleton( TweetListPresenter );

			// todo: Add Services

			// todo: Add View
            mediatorMap.mapView(MainView, MainViewMediator);

			// Startup complete
			dispatchEvent( new ContextEvent( ContextEvent.STARTUP ) );
		}

Take a look at the folder structure:

screen-shot-2010-01-23-at-113505-am

Feel free to download the full example from here, and let me know your thoughts.

Cheers :)


9 Responses to “RobotLegs Presentation Model implementation that access entire component events lifecycle”


  1. 1 Matt Jan 25th, 2010 at 4:07 pm

    Very nice solution, Elad. I’ll definitely be considering this for my Flex/AIR applications. Cheers!

  2. 2 Rick R Feb 13th, 2010 at 11:53 am

    This looks killer. Thanks for posting it. I’m new to Flex and RIAs in general (coming over from the web world:), and have been researching all different framworks - PureMVC, Mate, Cairngorm, Robotlegs, etc. Found this post from the Robotolegs site. I ‘think’ this is just what I’ve been looking for.

  3. 3 Fabien Feb 18th, 2010 at 11:22 am

    Hi Elad,

    Nice work on RobotLegs. However I’m wondering, is this really an implementation of the Presentation Model pattern? As Martin Fowler describes it (http://www.martinfowler.com/eaaDev/PresentationModel.html) and as I have always implemented it, the PM does not have any reference to the view, it just encapsulate the logic so this can be tested. The view then knows about the PM, but just rely on its “interface”.
    What you are implementing is more a Passive View implementation, where your view gets minimal (just having “view logic” like transition) so it can be highly reusable.

    Regards, Fabien

  4. 4 elad.ny Feb 18th, 2010 at 9:43 pm

    Hi Fabien,

    Thanks for the comment. I want to point out that there many ways to create an implementation of the presentation model pattern and in fact the passive view is a derivative of Model View Presenter. If you read Fowler article careful you will notice that he splits the Model View Presenter into separate patterns & highlight two common approaches (there may be more) for implementing the Model View Presenter. You can choose whatever you prefer and change this example to fit your need. I hope this help. Cheers :)

  5. 5 Amy Blankenship Jun 4th, 2010 at 2:29 pm

    Hi, Elad;

    I ran across this while looking for resources for my own implementation of Presentation Model, which I’ve posted here: http://flexdiary.magnoliamultimedia.com/RobotLegsHierarchicalRemoteObject/RobotLegsHierarchicalRemoteObject.html . If you get time, please take a look and tell me what you think.

  6. 6 elad.ny Jun 5th, 2010 at 8:22 pm

    Hi Amy, nice implementation :) can you please explain what type of presentation model is being implemented here? What’s the Presenter and view responsibilities? I personally prefer to create stand alone loosely coupled components, without being married to any framework. I would prefer to create classification as a loosely coupled component and than integrate it back into Robotlegs.

  7. 7 Amy Blankenship Jun 7th, 2010 at 11:09 am

    What I did is my understanding of _the_ Presentation Model Pattern (i.e., you break off a piece of the model that contains the model data and contains the data needed for a particular View, as well as all of the functionality needed to act on that piece of data. Then the View hooks onto that piece of the Model and operates on its API in response to user gestures). I modeled my implementation on what Paul Williams did in his blog here http://blogs.adobe.com/paulw/archives/2007/10/presentation_pa_3.html, and to a certain extent Pete Mackie’s Presentation Model Mate example with the Presentation Model here http://www.insideria.com/2010/02/photo-gallery-mate-framework-w.html.

    I think your implementation is a bit closer to Supervising Presenter http://blogs.adobe.com/paulw/archives/2007/10/presentation_pa_2.html. Neither your implementation nor mine is completely uncoupled, it’s just a question of which direction you prefer to have the coupling in.

    Since I have a background as a designer, I tend to believe that the specifics of the View implementation are going to change–probably right up until the last minute, whereas the underlying logic tends to be fairly static. So I think it’s preferable to let the View know about something that _won’t_ change–the logic, rather than having a logic Class that has to continually fluctuate based on the internals of a View that might, for instance, change something internally from a TextField to a TextArea.

    Also, I agree with Paul Williams in theory that it’s easier to test the logic that knows nothing about the View than one that knows everything about a specific view, but I’d defer to your greater experience on that count.

    I definitely think that the Presentation Model pattern could be modified to use less coupling than what you see in my example. For instance, you could provide public properties on the View, including Callback functions, and you could allow the Mediator to inject just those pieces of the Presentation Model into the View. Then the View would have no need to know where this functionality comes from. I’m not sure if that still qualifies as Presentation Model, though, or if there’s another name for such a thing.

  8. 8 Amy Blankenship Jun 11th, 2010 at 9:55 am

    Hey, Elad

    Wondering if you forgot about my pending comment? Seems odd to ask me a question in your comments, then not allow anyone to see the response…

  1. 1 blog.andreitt.com » Blog Archive » Robotlegs beginner Q&A Pingback on Jul 31st, 2010 at 7:32 am