Understanding the PureMVC Open Source Framework2

Now we need to think about when this mediator will come into use. Remember earlier I said that there's no point running a mediator before it's needed? Well this mediator isn't going to be run before we need it, so we're assuming that when it's first registered, the data will be ready for the view to build the buttons, so we update our "onRegister()" function to send the data to the view, show the view and add it to the stage. Since our data is stored within our "DataProxy" VO, we'll need to add another function which allows us to access the facade's instance of the proxy and retrieve data from the VO:

  1. override public function onRegister():void  
  2. {   
  3.     urlsView = new URLsView();   
  4.   
  5.     urlsView.addEventListener( URLsView.CLICKED, handleURLsViewClicked );   
  6.   
  7.     urlsView.init( proxy.vo.urlsArray );   
  8.   
  9.     urlsView.show();   
  10.   
  11.     viewComponent.addChild( urlsView );   
  12. }   
  13.   
  14. private function get proxy():DataProxy   
  15. {   
  16.     return facade.retrieveProxy( DataProxy.NAME ) as DataProxy;   
  17. }   
  18.   
		override public function onRegister():void
		{
			urlsView = new URLsView();

			urlsView.addEventListener( URLsView.CLICKED, handleURLsViewClicked );

			urlsView.init( proxy.vo.urlsArray );

			urlsView.show();

			viewComponent.addChild( urlsView );
		}
		
		private function get proxy():DataProxy
		{
			return facade.retrieveProxy( DataProxy.NAME ) as DataProxy;
		}
		
	

Finally, as this mediator will be created when the data's ready, we need to tell our "ProgressView" that we no longer want it to be visible, so we fire a notification called "ProgressView.HIDE" which will be picked up by the "ProgressViewMediator" and will tell "ProgressView" to hide itself:

  1. override public function onRegister():void  
  2. {   
  3.     urlsView = new URLsView();   
  4.   
  5.     urlsView.addEventListener( URLsView.CLICKED, handleURLsViewClicked );   
  6.   
  7.     urlsView.init( proxy.vo.urlsArray );   
  8.   
  9.     urlsView.show();   
  10.   
  11.     viewComponent.addChild( urlsView );   
  12.        
  13.     sendNotification( ProgressView.HIDE );   
  14. }   
		override public function onRegister():void
		{
			urlsView = new URLsView();

			urlsView.addEventListener( URLsView.CLICKED, handleURLsViewClicked );

			urlsView.init( proxy.vo.urlsArray );

			urlsView.show();

			viewComponent.addChild( urlsView );
			
			sendNotification( ProgressView.HIDE );
		}
	

Once again, before we can continue, we need to think about which notifications this mediator will need to listen to. Since we're going to make this a usable application, there's no point in not letting the user go back and pick another Flickr feed url, so it makes sense to allow this view to be shown again. This is where the public const SHOW comes in to play (you'll notice that I have a naming convention when it comes to all my notifications, this is a good thing and will speed up your development). Just like with our "ProgressViewMediator", we add "listNotificationInterests()" and "handleNotification()" functions to our class:

  1. package com.flashtuts.view   
  2. {   
  3.     import com.flashtuts.model.DataProxy;   
  4.     import com.flashtuts.view.component.ProgressView;   
  5.     import com.flashtuts.view.component.URLsView;   
  6.   
  7.     import flash.events.DataEvent;   
  8.   
  9.     import org.puremvc.as3.interfaces.IMediator;   
  10.     import org.puremvc.as3.interfaces.INotification;   
  11.     import org.puremvc.as3.patterns.mediator.Mediator;   
  12.   
  13.     public class URLsViewMediator extends Mediator implements IMediator   
  14.     {   
  15.         public static const NAME:String                         = 'URLsViewMediator';   
  16.   
  17.         private var urlsView:URLsView;   
  18.   
  19.         public function URLsViewMediator(viewComponent:Object=null)   
  20.         {   
  21.             super( NAME, viewComponent);   
  22.         }   
  23.   
  24.         override public function onRegister():void  
  25.         {   
  26.             urlsView = new URLsView();   
  27.   
  28.             urlsView.addEventListener( URLsView.CLICKED, handleURLsViewClicked );   
  29.   
  30.             urlsView.init( proxy.vo.urlsArray );   
  31.   
  32.             urlsView.show();   
  33.   
  34.             viewComponent.addChild( urlsView );   
  35.   
  36.             sendNotification( ProgressView.HIDE );   
  37.         }   
  38.   
  39.         override public function listNotificationInterests():Array   
  40.         {   
  41.             return [   
  42.                 URLsView.SHOW   
  43.             ];   
  44.         }   
  45.   
  46.         override public function handleNotification(notification:INotification):void  
  47.         {   
  48.             var name:String = notification.getName();   
  49.             var body:Object = notification.getBody();   
  50.   
  51.             switch ( name )   
  52.             {   
  53.                 case URLsView.SHOW:   
  54.                 urlsView.show();   
  55.   
  56.                 break;   
  57.             }   
  58.         }   
  59.   
  60.         private function handleURLsViewClicked(e:DataEvent):void  
  61.         {   
  62.             urlsView.hide();   
  63.   
  64.             sendNotification( URLsView.CLICKED, { index: e.data } );   
  65.         }   
  66.   
  67.         private function get proxy():DataProxy   
  68.         {   
  69.             return facade.retrieveProxy( DataProxy.NAME ) as DataProxy;   
  70.         }   
  71.     }   
  72. }   
		package com.flashtuts.view
		{
			import com.flashtuts.model.DataProxy;
			import com.flashtuts.view.component.ProgressView;
			import com.flashtuts.view.component.URLsView;

			import flash.events.DataEvent;

			import org.puremvc.as3.interfaces.IMediator;
			import org.puremvc.as3.interfaces.INotification;
			import org.puremvc.as3.patterns.mediator.Mediator;

			public class URLsViewMediator extends Mediator implements IMediator
			{
				public static const NAME:String							= 'URLsViewMediator';

				private var urlsView:URLsView;

				public function URLsViewMediator(viewComponent:Object=null)
				{
					super( NAME, viewComponent);
				}

				override public function onRegister():void
				{
					urlsView = new URLsView();

					urlsView.addEventListener( URLsView.CLICKED, handleURLsViewClicked );

					urlsView.init( proxy.vo.urlsArray );

					urlsView.show();

					viewComponent.addChild( urlsView );

					sendNotification( ProgressView.HIDE );
				}

				override public function listNotificationInterests():Array
				{
					return [
						URLsView.SHOW
					];
				}

				override public function handleNotification(notification:INotification):void
				{
					var name:String = notification.getName();
					var body:Object = notification.getBody();

					switch ( name )
					{
						case URLsView.SHOW:
						urlsView.show();

						break;
					}
				}

				private function handleURLsViewClicked(e:DataEvent):void
				{
					urlsView.hide();

					sendNotification( URLsView.CLICKED, { index: e.data } );
				}

				private function get proxy():DataProxy
				{
					return facade.retrieveProxy( DataProxy.NAME ) as DataProxy;
				}
			}
		}
	

You'll notice that I've added some stuff to the "handleContainerClick()" function. All this function does is simply pass the index of the button pressed (like 0, 1, 2...) with the event name "URLsView.CLICKED". We will handle this event shortly as this is the event we'll use to load the Flickr feed the user has picked.

Now that our view is ready for our data, we can proceed to the proxy and load some XML. Whoop whoop!

Step 11 - Loading Data

As I mentioned above, our "ProgressViewMediator" fires off a notification called "URLsView.DATA_GET". In order for our proxy to receive this notification, we need it to go via our facade and then a command that will then call the proxy's function. First then, we need to register the command within our facade, so open up "ApplicationFacade.as" and add the "registerCommand" function to the "initializeController()" function like so:

  1. package com.flashtuts   
  2. {   
  3.     import com.flashtuts.controller.*;   
  4.     import com.flashtuts.model.*;   
  5.     import com.flashtuts.view.*;   
  6.     import com.flashtuts.view.component.ImagesView;   
  7.     import com.flashtuts.view.component.URLsView;   
  8.   
  9.     import org.puremvc.as3.interfaces.IFacade;   
  10.     import org.puremvc.as3.patterns.facade.Facade;   
  11.     import org.puremvc.as3.patterns.observer.Notification;   
  12.   
  13.     public class ApplicationFacade extends Facade implements IFacade   
  14.     {   
  15.         public static const NAME:String                         = 'ApplicationFacade';   
  16.   
  17.         public static const STARTUP:String                      = NAME + 'StartUp';   
  18.   
  19.         public static function getInstance():ApplicationFacade   
  20.         {   
  21.             return (instance ? instance : new ApplicationFacade()) as ApplicationFacade;   
  22.         }   
  23.   
  24.         override protected function initializeController():void  
  25.         {   
  26.             super.initializeController();   
  27.   
  28.             registerCommand( STARTUP, StartupCommand );   
  29.   
  30.             registerCommand( URLsView.DATA_GET, DataCommand );   
  31.         }   
  32.   
  33.         public function startup(stage:Object):void  
  34.         {   
  35.             sendNotification( STARTUP,  stage );   
  36.         }   
  37.   
  38.         override public function sendNotification(notificationName:String, body:Object=null, type:String=null):void  
  39.         {   
  40.             trace( 'Sent ' + notificationName );   
  41.   
  42.             notifyObservers( new Notification( notificationName, body, type ) );   
  43.         }   
  44.     }   
  45. }   
		package com.flashtuts
		{
			import com.flashtuts.controller.*;
			import com.flashtuts.model.*;
			import com.flashtuts.view.*;
			import com.flashtuts.view.component.ImagesView;
			import com.flashtuts.view.component.URLsView;

			import org.puremvc.as3.interfaces.IFacade;
			import org.puremvc.as3.patterns.facade.Facade;
			import org.puremvc.as3.patterns.observer.Notification;

			public class ApplicationFacade extends Facade implements IFacade
			{
				public static const NAME:String							= 'ApplicationFacade';

				public static const STARTUP:String						= NAME + 'StartUp';

				public static function getInstance():ApplicationFacade
				{
					return (instance ? instance : new ApplicationFacade()) as ApplicationFacade;
				}

				override protected function initializeController():void
				{
					super.initializeController();

					registerCommand( STARTUP, StartupCommand );

					registerCommand( URLsView.DATA_GET, DataCommand );
				}

				public function startup(stage:Object):void
				{
					sendNotification( STARTUP, 	stage );
				}

				override public function sendNotification(notificationName:String, body:Object=null, type:String=null):void
				{
					trace( 'Sent ' + notificationName );

					notifyObservers( new Notification( notificationName, body, type ) );
				}
			}
		}
	

You'll see that we're telling our facade to pass this notification to a command called "DataCommand", this is the name of the controller we're going to make to handle this notification and run the proxy. Next, create a file called "DataCommand.as" in "src/com/flashtuts/controller" and it'll need to extend SimpleCommand and implement ICommand. Our class won't need a constructor as we're interested in the "execute()" function (just like the "StartupCommand"):

  1. package com.flashtuts.controller   
  2. {   
  3.     import org.puremvc.as3.interfaces.ICommand;   
  4.     import org.puremvc.as3.interfaces.INotification;   
  5.     import org.puremvc.as3.patterns.command.SimpleCommand;   
  6.   
  7.     public class DataCommand extends SimpleCommand implements ICommand   
  8.     {   
  9.         override public function execute(notification:INotification):void  
  10.         {   
  11.                
  12.         }   
  13.     }   
  14. }   
		package com.flashtuts.controller
		{
			import org.puremvc.as3.interfaces.ICommand;
			import org.puremvc.as3.interfaces.INotification;
			import org.puremvc.as3.patterns.command.SimpleCommand;

			public class DataCommand extends SimpleCommand implements ICommand
			{
				override public function execute(notification:INotification):void
				{
					
				}
			}
		}
	

If you have a sharp eye, you'll notice that we're facing our friend the INotification class. Just as with a mediator, this class gives us the name and body of a notification, so we handle it in the same way - with a switch:

  1. package com.flashtuts.controller   
  2. {   
  3.     import com.flashtuts.view.component.URLsView;   
  4.   
  5.     import org.puremvc.as3.interfaces.ICommand;   
  6.     import org.puremvc.as3.interfaces.INotification;   
  7.     import org.puremvc.as3.patterns.command.SimpleCommand;   
  8.   
  9.     public class DataCommand extends SimpleCommand implements ICommand   
  10.     {   
  11.         override public function execute(notification:INotification):void  
  12.         {   
  13.             var name:String = notification.getName();   
  14.             var body:Object = notification.getBody();   
  15.   
  16.             switch ( name )   
  17.             {   
  18.                 case URLsView.DATA_GET:   
  19.   
  20.                 break;   
  21.             }   
  22.         }   
  23.     }   
  24. }   
		package com.flashtuts.controller
		{
			import com.flashtuts.view.component.URLsView;

			import org.puremvc.as3.interfaces.ICommand;
			import org.puremvc.as3.interfaces.INotification;
			import org.puremvc.as3.patterns.command.SimpleCommand;

			public class DataCommand extends SimpleCommand implements ICommand
			{
				override public function execute(notification:INotification):void
				{
					var name:String = notification.getName();
					var body:Object = notification.getBody();

					switch ( name )
					{
						case URLsView.DATA_GET:

						break;
					}
				}
			}
		}
	

Since we want that notification to tell the proxy to load some data, we need to get the facade's instance of the proxy and get it to fire a function. We use the same method we used within our mediator and create a get function:

  1. package com.flashtuts.controller   
  2. {   
  3.     import com.flashtuts.model.DataProxy;   
  4.     import com.flashtuts.view.component.URLsView;   
  5.   
  6.     import org.puremvc.as3.interfaces.ICommand;   
  7.     import org.puremvc.as3.interfaces.INotification;   
  8.     import org.puremvc.as3.patterns.command.SimpleCommand;   
  9.   
  10.     public class DataCommand extends SimpleCommand implements ICommand   
  11.     {   
  12.         override public function execute(notification:INotification):void  
  13.         {   
  14.             var name:String = notification.getName();   
  15.             var body:Object = notification.getBody();   
  16.   
  17.             switch ( name )   
  18.             {   
  19.                 case URLsView.DATA_GET:   
  20.                 proxy.urlsDataGet();   
  21.   
  22.                 break;   
  23.             }   
  24.         }   
  25.   
  26.         private function get proxy():DataProxy   
  27.         {   
  28.             return facade.retrieveProxy( DataProxy.NAME ) as DataProxy;   
  29.         }   
  30.     }   
  31. }   
		package com.flashtuts.controller
		{
			import com.flashtuts.model.DataProxy;
			import com.flashtuts.view.component.URLsView;

			import org.puremvc.as3.interfaces.ICommand;
			import org.puremvc.as3.interfaces.INotification;
			import org.puremvc.as3.patterns.command.SimpleCommand;

			public class DataCommand extends SimpleCommand implements ICommand
			{
				override public function execute(notification:INotification):void
				{
					var name:String = notification.getName();
					var body:Object = notification.getBody();

					switch ( name )
					{
						case URLsView.DATA_GET:
						proxy.urlsDataGet();

						break;
					}
				}

				private function get proxy():DataProxy
				{
					return facade.retrieveProxy( DataProxy.NAME ) as DataProxy;
				}
			}
		}
	

Finally, you'll see that we're calling a function within our proxy called "urlsDataGet()". This will load our data, so we'd better create it. Open up "DataProxy.as" and create a function called "urlsDataGet()" which will load the data, like so:

  1. package com.flashtuts.model   
  2. {   
  3.     import com.flashtuts.model.vo.DataVO;   
  4.     import com.flashtuts.view.component.ProgressView;   
  5.     import com.flashtuts.view.component.URLsView;   
  6.   
  7.     import flash.display.LoaderInfo;   
  8.     import flash.events.Event;   
  9.     import flash.events.ProgressEvent;   
  10.     import flash.net.URLLoader;   
  11.     import flash.net.URLRequest;   
  12.     import flash.utils.Dictionary;   
  13.   
  14.     import org.puremvc.as3.interfaces.IProxy;   
  15.     import org.puremvc.as3.patterns.proxy.Proxy;   
  16.   
  17.     public class DataProxy extends Proxy implements IProxy   
  18.     {   
  19.         public static const NAME:String                         = 'DataProxy';   
  20.   
  21.         private var indexDic:Dictionary = new Dictionary();   
  22.   
  23.         public function DataProxy()   
  24.         {   
  25.             super( NAME, new DataVO() );   
  26.         }   
  27.   
  28.         public function urlsDataGet():void  
  29.         {   
  30.             var request:URLRequest = new URLRequest();   
  31.             var loader:URLLoader = new URLLoader();   
  32.   
  33.             sendNotification( ProgressView.SHOW );   
  34.   
  35.             request.url = vo.dataURL;   
  36.   
  37.             loader.addEventListener( ProgressEvent.PROGRESS, handleProgress );             
  38.             loader.addEventListener( Event.COMPLETE, handleURLsDataGetComplete );   
  39.   
  40.             loader.load( request );   
  41.         }   
  42.   
  43.         private function handleURLsDataGetComplete(e:Event):void  
  44.         {   
  45.             var data:XML = new XML( e.target.data );   
  46.   
  47.             for each ( var url:XML in data..url )   
  48.             {   
  49.                 vo.urlsArray.push( url.toString() );   
  50.                 vo.urlsDataArray.push( '' );   
  51.             }   
  52.   
  53.             sendNotification( URLsView.DATA_READY );   
  54.         }   
  55.   
  56.         private function handleProgress(e:ProgressEvent):void  
  57.         {   
  58.             sendNotification( ProgressView.UPDATE, { percent: Math.round( ( e.bytesLoaded / e.bytesTotal ) * 100 ) } );   
  59.         }   
  60.   
  61.         public function get vo():DataVO   
  62.         {   
  63.             return data as DataVO;   
  64.         }   
  65.     }   
  66. }   
		package com.flashtuts.model
		{
			import com.flashtuts.model.vo.DataVO;
			import com.flashtuts.view.component.ProgressView;
			import com.flashtuts.view.component.URLsView;

			import flash.display.LoaderInfo;
			import flash.events.Event;
			import flash.events.ProgressEvent;
			import flash.net.URLLoader;
			import flash.net.URLRequest;
			import flash.utils.Dictionary;

			import org.puremvc.as3.interfaces.IProxy;
			import org.puremvc.as3.patterns.proxy.Proxy;

			public class DataProxy extends Proxy implements IProxy
			{
				public static const NAME:String							= 'DataProxy';

				private var indexDic:Dictionary = new Dictionary();

				public function DataProxy()
				{
					super( NAME, new DataVO() );
				}

				public function urlsDataGet():void
				{
					var request:URLRequest = new URLRequest();
					var loader:URLLoader = new URLLoader();

					sendNotification( ProgressView.SHOW );

					request.url = vo.dataURL;

					loader.addEventListener( ProgressEvent.PROGRESS, handleProgress );			
					loader.addEventListener( Event.COMPLETE, handleURLsDataGetComplete );

					loader.load( request );
				}

				private function handleURLsDataGetComplete(e:Event):void
				{
					var data:XML = new XML( e.target.data );

					for each ( var url:XML in data..url )
					{
						vo.urlsArray.push( url.toString() );
						vo.urlsDataArray.push( '' );
					}

					sendNotification( URLsView.DATA_READY );
				}

				private function handleProgress(e:ProgressEvent):void
				{
					sendNotification( ProgressView.UPDATE, { percent: Math.round( ( e.bytesLoaded / e.bytesTotal ) * 100 ) } );
				}

				public function get vo():DataVO
				{
					return data as DataVO;
				}
			}
		}
	

You'll notice that we're making use of our VO here by again creating a get function so that we can use and add data to it. You should be familiar with loading XML data so I won't walk through the functions; you should be able to see what they do. You may be wondering why I'm running through a loop of the urls from the XML data and populating an array with an empty string, you'll find out later...

The main things I will mention are the "handleProgress()" and "handleURLsDataGetComplete()" functions. They both send notifications to the application, the first sends a percentage of the data load to our progress view (remember I said a notification is made up of a name and body?) and the latter sends a notification to the application stating that our first bit of data has finished loading.

Finally, because we only want our "URLsViewMediator" and "URLsView" to be registered when the data is ready, we need to amend the application mediator to register the mediator when that event is sent:

  1. package com.flashtuts.view   
  2. {   
  3.     import com.flashtuts.view.component.ProgressView;   
  4.     import com.flashtuts.view.component.URLsView;   
  5.   
  6.     import org.puremvc.as3.interfaces.IMediator;   
  7.     import org.puremvc.as3.interfaces.INotification;   
  8.     import org.puremvc.as3.patterns.mediator.Mediator;   
  9.   
  10.     public class ApplicationMediator extends Mediator implements IMediator   
  11.     {   
  12.         public static const NAME:String                         = 'ApplicationMediator';   
  13.   
  14.         public function ApplicationMediator(viewComponent:Object=null)   
  15.         {   
  16.             super( NAME, viewComponent );   
  17.         }   
  18.   
  19.         override public function onRegister():void  
  20.         {   
  21.             facade.registerMediator( new ProgressViewMediator( viewComponent ) );   
  22.         }   
  23.   
  24.         override public function listNotificationInterests():Array   
  25.         {   
  26.             return [   
  27.                 URLsView.DATA_READY   
  28.             ];   
  29.         }   
  30.   
  31.         override public function handleNotification(notification:INotification):void  
  32.         {   
  33.             var name:String = notification.getName();   
  34.             var body:Object = notification.getBody();   
  35.   
  36.             switch ( name )   
  37.             {   
  38.                 case URLsView.DATA_READY:   
  39.                 facade.registerMediator( new URLsViewMediator( viewComponent ) );   
  40.   
  41.                 break;   
  42.             }   
  43.         }   
  44.     }   
  45. }   
		package com.flashtuts.view
		{
			import com.flashtuts.view.component.ProgressView;
			import com.flashtuts.view.component.URLsView;

			import org.puremvc.as3.interfaces.IMediator;
			import org.puremvc.as3.interfaces.INotification;
			import org.puremvc.as3.patterns.mediator.Mediator;

			public class ApplicationMediator extends Mediator implements IMediator
			{
				public static const NAME:String							= 'ApplicationMediator';

				public function ApplicationMediator(viewComponent:Object=null)
				{
					super( NAME, viewComponent );
				}

				override public function onRegister():void
				{
					facade.registerMediator( new ProgressViewMediator( viewComponent ) );
				}

				override public function listNotificationInterests():Array
				{
					return [
						URLsView.DATA_READY
					];
				}

				override public function handleNotification(notification:INotification):void
				{
					var name:String = notification.getName();
					var body:Object = notification.getBody();

					switch ( name )
					{
						case URLsView.DATA_READY:
						facade.registerMediator( new URLsViewMediator( viewComponent ) );

						break;
					}
				}
			}
		}
	

That code shouldn't be too unfamiliar, but as you can see we're setting which notifications we want it to listen to and then handling them. In this case registering the "URLsViewMediator" which runs its "onRegister()" function and builds the view.

We're at the next mile stone! Now we should see that our application will load the XML data and then pass this data to our "URLsViewMediator" which will in turn tell the "URLsView" to create some buttons ready for the user to click:

Give yourself a pat on the back as you've achieved a lot! By now you should be familiar with how notifications play a big part in the application and how the whole structure is brought together by the facade. The next bit will be a breeze...

Step 12 - Handling a User Event

As I explained earlier, a view will dispatch a user event, such as mouse move or in this case mouse click. This event will then be picked up by its mediator which then decides what to do. Since we've set up the function "handleContainerClick()" within "URLsViewMediator" to send an index of the button, we now need to handle that event and load the subsequent data. First we'll need to build our final view and mediator.

Here's the view:

  1. package com.flashtuts.view.component   
  2. {   
  3.     import flash.display.Loader;   
  4.     import flash.display.Sprite;   
  5.     import flash.events.Event;   
  6.     import flash.events.MouseEvent;   
  7.     import flash.net.URLRequest;   
  8.     import flash.text.TextField;   
  9.     import flash.text.TextFieldAutoSize;   
  10.     import flash.text.TextFormat;   
  11.   
  12.     import gs.TweenLite;   
  13.   
  14.     public class ImagesView extends Sprite   
  15.     {   
  16.         public static const NAME:String                         = 'ImagesView';   
  17.   
  18.         public static const DATA_GET:String                     = NAME + 'DataGet';   
  19.         public static const DATA_READY:String                   = NAME + 'DataReady';   
  20.         public static const SHOW:String                         = NAME + 'Show';   
  21.         public static const HIDE:String                         = NAME + 'Hide';   
  22.         public static const GO_BACK_CLICKED:String              = NAME + 'GoBackClicked';   
  23.   
  24.         public function init(images:XML):void  
  25.         {   
  26.             var maxImages:Number = 15;   
  27.             var perRow:Number = 5;   
  28.             var xRowCount:Number = 0;   
  29.             var yRowCount:Number = 0;   
  30.             var element:XML;   
  31.             var request:URLRequest;   
  32.             var loader:Loader;   
  33.   
  34.             for ( var i:Number = 0; i < maxImages; i++ )   
  35.             {   
  36.                 element = images..photo[ i ];   
  37.   
  38.                 request = new URLRequest();   
  39.   
  40.                 request.url = 'http://farm' + element.@farm + '.static.flickr.com/' + element.@server + '/' + element.@id + '_' +element.@secret + '_t.jpg';   
  41.   
  42.                 loader = new Loader();     
  43.   
  44.                 if ( xRowCount == perRow )   
  45.                 {   
  46.                     xRowCount = 0;   
  47.                     yRowCount++;   
  48.                 }   
  49.   
  50.                 loader.contentLoaderInfo.addEventListener( Event.COMPLETE, handleLoaderComplete );   
  51.   
  52.                 loader.alpha = 1;   
  53.                 loader.x = xRowCount * 120;   
  54.                 loader.y = yRowCount * 120;   
  55.   
  56.                 loader.load( request );   
  57.   
  58.                 xRowCount++;   
  59.   
  60.                 addChild( loader );   
  61.             }   
  62.   
  63.             addBackButton();   
  64.   
  65.             alpha = 0;   
  66.         }   
  67.   
  68.         private function handleLoaderComplete(e:Event):void  
  69.         {   
  70.             TweenLite.from( e.target.content, .5, { autoAlpha: 0 } );   
  71.         }   
  72.   
  73.         private function addBackButton():void  
  74.         {   
  75.             var textFormat:TextFormat = new TextFormat();   
  76.             var textContainer:Sprite = new Sprite();   
  77.             var textField:TextField = new TextField();   
  78.   
  79.             textFormat.color = 0xFFFFFF;   
  80.             textFormat.font = 'Arial';   
  81.   
  82.             textContainer.addEventListener( MouseEvent.CLICK, handleContainerClick );   
  83.   
  84.             textContainer.buttonMode = true;   
  85.             textContainer.graphics.lineStyle( 1, 0xFFFFFF );   
  86.             textContainer.graphics.beginFill( 0x333333 );   
  87.             textContainer.graphics.drawRoundRect( 0, 0, 150, 30, 5, 5 );   
  88.             textContainer.graphics.endFill();   
  89.             textContainer.mouseChildren = false;   
  90.             textContainer.x = 430;   
  91.             textContainer.y = 350;   
  92.   
  93.             textField.autoSize = TextFieldAutoSize.CENTER;   
  94.             textField.defaultTextFormat = textFormat;   
  95.             textField.embedFonts = true;   
  96.             textField.text = 'Go back';   
  97.             textField.x = 75 - ( textField.width / 2 );   
  98.             textField.y = 15 - ( textField.height / 2 );   
  99.   
  100.             textContainer.addChild( textField );   
  101.   
  102.             addChild( textContainer );   
  103.         }   
  104.   
  105.         private function handleContainerClick(e:MouseEvent=null):void  
  106.         {   
  107.             if ( numChildren > 0 )   
  108.             {   
  109.                 for ( var i:Number = 0; i < numChildren; i++ )   
  110.                 {   
  111.                     removeChildAt( i );   
  112.                 }   
  113.   
  114.                 handleContainerClick();   
  115.             }   
  116.             else  
  117.             {   
  118.                 dispatchEvent( new Event( GO_BACK_CLICKED, true ) );   
  119.             }   
  120.         }   
  121.   
  122.         public function show():void  
  123.         {   
  124.             TweenLite.to( this, .5, { autoAlpha: 1 } );   
  125.         }   
  126.   
  127.         public function hide():void  
  128.         {   
  129.             TweenLite.to( this, .5, { autoAlpha: 0 } );   
  130.         }   
  131.     }   
  132. }   
		package com.flashtuts.view.component
		{
			import flash.display.Loader;
			import flash.display.Sprite;
			import flash.events.Event;
			import flash.events.MouseEvent;
			import flash.net.URLRequest;
			import flash.text.TextField;
			import flash.text.TextFieldAutoSize;
			import flash.text.TextFormat;

			import gs.TweenLite;

			public class ImagesView extends Sprite
			{
				public static const NAME:String							= 'ImagesView';

				public static const DATA_GET:String						= NAME + 'DataGet';
				public static const DATA_READY:String					= NAME + 'DataReady';
				public static const SHOW:String							= NAME + 'Show';
				public static const HIDE:String							= NAME + 'Hide';
				public static const GO_BACK_CLICKED:String				= NAME + 'GoBackClicked';

				public function init(images:XML):void
				{
					var maxImages:Number = 15;
					var perRow:Number = 5;
					var xRowCount:Number = 0;
					var yRowCount:Number = 0;
					var element:XML;
					var request:URLRequest;
					var loader:Loader;

					for ( var i:Number = 0; i < maxImages; i++ )
					{
						element = images..photo[ i ];

						request = new URLRequest();

						request.url = 'http://farm' + element.@farm + '.static.flickr.com/' + element.@server + '/' + element.@id + '_' +element.@secret + '_t.jpg';

						loader = new Loader();	

						if ( xRowCount == perRow )
						{
							xRowCount = 0;
							yRowCount++;
						}

						loader.contentLoaderInfo.addEventListener( Event.COMPLETE, handleLoaderComplete );

						loader.alpha = 1;
						loader.x = xRowCount * 120;
						loader.y = yRowCount * 120;

						loader.load( request );

						xRowCount++;

						addChild( loader );
					}

					addBackButton();

					alpha = 0;
				}

				private function handleLoaderComplete(e:Event):void
				{
					TweenLite.from( e.target.content, .5, { autoAlpha: 0 } );
				}

				private function addBackButton():void
				{
					var textFormat:TextFormat = new TextFormat();
					var textContainer:Sprite = new Sprite();
					var textField:TextField = new TextField();

					textFormat.color = 0xFFFFFF;
					textFormat.font = 'Arial';

					textContainer.addEventListener( MouseEvent.CLICK, handleContainerClick );

					textContainer.buttonMode = true;
					textContainer.graphics.lineStyle( 1, 0xFFFFFF );
					textContainer.graphics.beginFill( 0x333333 );
					textContainer.graphics.drawRoundRect( 0, 0, 150, 30, 5, 5 );
					textContainer.graphics.endFill();
					textContainer.mouseChildren = false;
					textContainer.x = 430;
					textContainer.y = 350;

					textField.autoSize = TextFieldAutoSize.CENTER;
					textField.defaultTextFormat = textFormat;
					textField.embedFonts = true;
					textField.text = 'Go back';
					textField.x = 75 - ( textField.width / 2 );
					textField.y = 15 - ( textField.height / 2 );

					textContainer.addChild( textField );

					addChild( textContainer );
				}

				private function handleContainerClick(e:MouseEvent=null):void
				{
					if ( numChildren > 0 )
					{
						for ( var i:Number = 0; i < numChildren; i++ )
						{
							removeChildAt( i );
						}

						handleContainerClick();
					}
					else
					{
						dispatchEvent( new Event( GO_BACK_CLICKED, true ) );
					}
				}

				public function show():void
				{
					TweenLite.to( this, .5, { autoAlpha: 1 } );
				}

				public function hide():void
				{
					TweenLite.to( this, .5, { autoAlpha: 0 } );
				}
			}
		}
	

All this view does is take the XML data loaded in from the Flickr API, build a grid of images and a back button. Here's the mediator:

  1. package com.flashtuts.view   
  2. {   
  3.     import com.flashtuts.model.DataProxy;   
  4.     import com.flashtuts.view.component.ImagesView;   
  5.     import com.flashtuts.view.component.ProgressView;   
  6.     import com.flashtuts.view.component.URLsView;   
  7.   
  8.     import flash.events.Event;   
  9.   
  10.     import org.puremvc.as3.interfaces.IMediator;   
  11.     import org.puremvc.as3.interfaces.INotification;   
  12.     import org.puremvc.as3.patterns.mediator.Mediator;   
  13.   
  14.     public class ImagesViewMediator extends Mediator implements IMediator   
  15.     {   
  16.         public static const NAME:String                         = 'ImagesViewMediator';   
  17.   
  18.         private var imagesView:ImagesView;   
  19.   
  20.         public function ImagesViewMediator(viewComponent:Object=null)   
  21.         {   
  22.             super( NAME, viewComponent );   
  23.         }   
  24.   
  25.         override public function onRegister():void  
  26.         {   
  27.             imagesView = new ImagesView();   
  28.   
  29.             imagesView.addEventListener( ImagesView.GO_BACK_CLICKED, handleImagesViewGoBackClicked );   
  30.   
  31.             viewComponent.addChild( imagesView );   
  32.         }   
  33.   
  34.         override public function listNotificationInterests():Array   
  35.         {   
  36.             return [   
  37.                 ImagesView.DATA_READY   
  38.             ];   
  39.         }   
  40.   
  41.         override public function handleNotification(notification:INotification):void  
  42.         {   
  43.             var name:String = notification.getName();   
  44.             var body:Object = notification.getBody();   
  45.   
  46.             switch ( name )   
  47.             {   
  48.                 case ImagesView.DATA_READY:   
  49.                 imagesView.init( proxy.vo.urlsDataArray[ body.index ] );   
  50.   
  51.                 imagesView.show();   
  52.   
  53.                 sendNotification( ProgressView.HIDE );   
  54.   
  55.                 break;   
  56.             }   
  57.         }   
  58.   
  59.         private function handleImagesViewGoBackClicked(e:Event):void  
  60.         {   
  61.             imagesView.hide();   
  62.   
  63.             sendNotification( URLsView.SHOW );   
  64.         }   
  65.   
  66.         private function get proxy():DataProxy   
  67.         {   
  68.             return facade.retrieveProxy( DataProxy.NAME ) as DataProxy;   
  69.         }   
  70.     }   
  71. }   
		package com.flashtuts.view
		{
			import com.flashtuts.model.DataProxy;
			import com.flashtuts.view.component.ImagesView;
			import com.flashtuts.view.component.ProgressView;
			import com.flashtuts.view.component.URLsView;

			import flash.events.Event;

			import org.puremvc.as3.interfaces.IMediator;
			import org.puremvc.as3.interfaces.INotification;
			import org.puremvc.as3.patterns.mediator.Mediator;

			public class ImagesViewMediator extends Mediator implements IMediator
			{
				public static const NAME:String							= 'ImagesViewMediator';

				private var imagesView:ImagesView;

				public function ImagesViewMediator(viewComponent:Object=null)
				{
					super( NAME, viewComponent );
				}

				override public function onRegister():void
				{
					imagesView = new ImagesView();

					imagesView.addEventListener( ImagesView.GO_BACK_CLICKED, handleImagesViewGoBackClicked );

					viewComponent.addChild( imagesView );
				}

				override public function listNotificationInterests():Array
				{
					return [
						ImagesView.DATA_READY
					];
				}

				override public function handleNotification(notification:INotification):void
				{
					var name:String = notification.getName();
					var body:Object = notification.getBody();

					switch ( name )
					{
						case ImagesView.DATA_READY:
						imagesView.init( proxy.vo.urlsDataArray[ body.index ] );

						imagesView.show();

						sendNotification( ProgressView.HIDE );

						break;
					}
				}

				private function handleImagesViewGoBackClicked(e:Event):void
				{
					imagesView.hide();

					sendNotification( URLsView.SHOW );
				}

				private function get proxy():DataProxy
				{
					return facade.retrieveProxy( DataProxy.NAME ) as DataProxy;
				}
			}
		}
	

Again, none of this should be new to you as we're simply creating an instance of our view, adding it to the stage, adding listeners to our view (and their handlers) and then setting out the notifications its going to listen to and how it will handle them.

In order to to handle this notification, we update the application mediator one last time and set it to listen to the "URLsView.CLICKED" notification:

  1. package com.flashtuts.view   
  2. {   
  3.     import com.flashtuts.view.component.ImagesView;   
  4.     import com.flashtuts.view.component.ProgressView;   
  5.     import com.flashtuts.view.component.URLsView;   
  6.   
  7.     import org.puremvc.as3.interfaces.IMediator;   
  8.     import org.puremvc.as3.interfaces.INotification;   
  9.     import org.puremvc.as3.patterns.mediator.Mediator;   
  10.   
  11.     public class ApplicationMediator extends Mediator implements IMediator   
  12.     {   
  13.         public static const NAME:String                         = 'ApplicationMediator';   
  14.   
  15.         public function ApplicationMediator(viewComponent:Object=null)   
  16.         {   
  17.             super( NAME, viewComponent );   
  18.         }   
  19.   
  20.         override public function onRegister():void  
  21.         {   
  22.             facade.registerMediator( new ProgressViewMediator( viewComponent ) );   
  23.         }   
  24.   
  25.         override public function listNotificationInterests():Array   
  26.         {   
  27.             return [   
  28.                 URLsView.DATA_READY,   
  29.                 URLsView.CLICKED   
  30.             ];   
  31.         }   
  32.   
  33.         override public function handleNotification(notification:INotification):void  
  34.         {   
  35.             var name:String = notification.getName();   
  36.             var body:Object = notification.getBody();   
  37.   
  38.             switch ( name )   
  39.             {   
  40.                 case URLsView.DATA_READY:   
  41.                 facade.registerMediator( new URLsViewMediator( viewComponent ) );   
  42.   
  43.                 break;   
  44.   
  45.                 case URLsView.CLICKED:   
  46.                 if ( !facade.hasMediator( ImagesViewMediator.NAME ) )   
  47.                 {   
  48.                     facade.registerMediator( new ImagesViewMediator( viewComponent ) );   
  49.                 }   
  50.   
  51.                 sendNotification( ImagesView.DATA_GET, body );                 
  52.   
  53.                 break;   
  54.             }   
  55.         }   
  56.     }   
  57. }   
		package com.flashtuts.view
		{
			import com.flashtuts.view.component.ImagesView;
			import com.flashtuts.view.component.ProgressView;
			import com.flashtuts.view.component.URLsView;

			import org.puremvc.as3.interfaces.IMediator;
			import org.puremvc.as3.interfaces.INotification;
			import org.puremvc.as3.patterns.mediator.Mediator;

			public class ApplicationMediator extends Mediator implements IMediator
			{
				public static const NAME:String							= 'ApplicationMediator';

				public function ApplicationMediator(viewComponent:Object=null)
				{
					super( NAME, viewComponent );
				}

				override public function onRegister():void
				{
					facade.registerMediator( new ProgressViewMediator( viewComponent ) );
				}

				override public function listNotificationInterests():Array
				{
					return [
						URLsView.DATA_READY,
						URLsView.CLICKED
					];
				}

				override public function handleNotification(notification:INotification):void
				{
					var name:String = notification.getName();
					var body:Object = notification.getBody();

					switch ( name )
					{
						case URLsView.DATA_READY:
						facade.registerMediator( new URLsViewMediator( viewComponent ) );

						break;

						case URLsView.CLICKED:
						if ( !facade.hasMediator( ImagesViewMediator.NAME ) )
						{
							facade.registerMediator( new ImagesViewMediator( viewComponent ) );
						}

						sendNotification( ImagesView.DATA_GET, body );				

						break;
					}
				}
			}
		}
	

One thing to note about the handling of this event: the first thing the code does is check if the mediator has been registered. If not, it registers and everyone gets on with it. The reason for this, as opposed to re-registering it is that we're going to create a back button for the user to be able to go back and pick another Flickr feed. Since we only need to show and hide the buttons that allow the user to choose, there's no need to rebuild them. However, since the user will then potentially choose another Flickr feed, we need to rebuild our images. To save duplicating our code, we only build images when the "ImagesViewMediator" has received the event "ImagesView.DATA_READY".

Since our application sends "ImagesView.DATA_GET" whenever the user selects a Flickr API, we need to adjust "ApplicationFacade", "DataCommand" and "DataProxy" to a) handle the event and b) load the data and send an event back.

We first edit "ApplicationFacade" adding a "registerCommand()" to the "initializeController()" function:

  1. package com.flashtuts   
  2. {   
  3.     import com.flashtuts.controller.*;   
  4.     import com.flashtuts.model.*;   
  5.     import com.flashtuts.view.*;   
  6.     import com.flashtuts.view.component.ImagesView;   
  7.     import com.flashtuts.view.component.URLsView;   
  8.   
  9.     import org.puremvc.as3.interfaces.IFacade;   
  10.     import org.puremvc.as3.patterns.facade.Facade;   
  11.     import org.puremvc.as3.patterns.observer.Notification;   
  12.   
  13.     public class ApplicationFacade extends Facade implements IFacade   
  14.     {   
  15.         public static const NAME:String                         = 'ApplicationFacade';   
  16.   
  17.         public static const STARTUP:String                      = NAME + 'StartUp';   
  18.   
  19.         public static function getInstance():ApplicationFacade   
  20.         {   
  21.             return (instance ? instance : new ApplicationFacade()) as ApplicationFacade;   
  22.         }   
  23.   
  24.         override protected function initializeController():void  
  25.         {   
  26.             super.initializeController();   
  27.   
  28.             registerCommand( STARTUP, StartupCommand );   
  29.   
  30.             registerCommand( URLsView.DATA_GET, DataCommand );   
  31.             registerCommand( ImagesView.DATA_GET, DataCommand );   
  32.         }   
  33.   
  34.         public function startup(stage:Object):void  
  35.         {   
  36.             sendNotification( STARTUP,  stage );   
  37.         }   
  38.   
  39.         override public function sendNotification(notificationName:String, body:Object=null, type:String=null):void  
  40.         {   
  41.             trace( 'Sent ' + notificationName );   
  42.   
  43.             notifyObservers( new Notification( notificationName, body, type ) );   
  44.         }   
  45.     }   
  46. }   
		package com.flashtuts
		{
			import com.flashtuts.controller.*;
			import com.flashtuts.model.*;
			import com.flashtuts.view.*;
			import com.flashtuts.view.component.ImagesView;
			import com.flashtuts.view.component.URLsView;

			import org.puremvc.as3.interfaces.IFacade;
			import org.puremvc.as3.patterns.facade.Facade;
			import org.puremvc.as3.patterns.observer.Notification;

			public class ApplicationFacade extends Facade implements IFacade
			{
				public static const NAME:String							= 'ApplicationFacade';

				public static const STARTUP:String						= NAME + 'StartUp';

				public static function getInstance():ApplicationFacade
				{
					return (instance ? instance : new ApplicationFacade()) as ApplicationFacade;
				}

				override protected function initializeController():void
				{
					super.initializeController();

					registerCommand( STARTUP, StartupCommand );

					registerCommand( URLsView.DATA_GET, DataCommand );
					registerCommand( ImagesView.DATA_GET, DataCommand );
				}

				public function startup(stage:Object):void
				{
					sendNotification( STARTUP, 	stage );
				}

				override public function sendNotification(notificationName:String, body:Object=null, type:String=null):void
				{
					trace( 'Sent ' + notificationName );

					notifyObservers( new Notification( notificationName, body, type ) );
				}
			}
		}
	

We then tell the "DataCommand" how to handle it (through the proxy):

  1. package com.flashtuts.controller   
  2. {   
  3.     import com.flashtuts.model.DataProxy;   
  4.     import com.flashtuts.view.component.ImagesView;   
  5.     import com.flashtuts.view.component.URLsView;   
  6.   
  7.     import org.puremvc.as3.interfaces.ICommand;   
  8.     import org.puremvc.as3.interfaces.INotification;   
  9.     import org.puremvc.as3.patterns.command.SimpleCommand;   
  10.   
  11.     public class DataCommand extends SimpleCommand implements ICommand   
  12.     {   
  13.         override public function execute(notification:INotification):void  
  14.         {   
  15.             var name:String = notification.getName();   
  16.             var body:Object = notification.getBody();   
  17.   
  18.             switch ( name )   
  19.             {   
  20.                 case URLsView.DATA_GET:   
  21.                 proxy.urlsDataGet();   
  22.   
  23.                 break;   
  24.   
  25.                 case ImagesView.DATA_GET:   
  26.                 proxy.imagesDataGet( body.index );   
  27.   
  28.                 break;   
  29.             }   
  30.         }   
  31.   
  32.         private function get proxy():DataProxy   
  33.         {   
  34.             return facade.retrieveProxy( DataProxy.NAME ) as DataProxy;   
  35.         }   
  36.     }   
  37. }   
		package com.flashtuts.controller
		{
			import com.flashtuts.model.DataProxy;
			import com.flashtuts.view.component.ImagesView;
			import com.flashtuts.view.component.URLsView;

			import org.puremvc.as3.interfaces.ICommand;
			import org.puremvc.as3.interfaces.INotification;
			import org.puremvc.as3.patterns.command.SimpleCommand;

			public class DataCommand extends SimpleCommand implements ICommand
			{
				override public function execute(notification:INotification):void
				{
					var name:String = notification.getName();
					var body:Object = notification.getBody();

					switch ( name )
					{
						case URLsView.DATA_GET:
						proxy.urlsDataGet();

						break;

						case ImagesView.DATA_GET:
						proxy.imagesDataGet( body.index );

						break;
					}
				}

				private function get proxy():DataProxy
				{
					return facade.retrieveProxy( DataProxy.NAME ) as DataProxy;
				}
			}
		}
	

Finally we edit the "DataProxy":

  1. package com.flashtuts.model   
  2. {   
  3.     import com.flashtuts.model.vo.DataVO;   
  4.     import com.flashtuts.view.component.ImagesView;   
  5.     import com.flashtuts.view.component.ProgressView;   
  6.     import com.flashtuts.view.component.URLsView;   
  7.   
  8.     import flash.display.LoaderInfo;   
  9.     import flash.events.Event;   
  10.     import flash.events.ProgressEvent;   
  11.     import flash.net.URLLoader;   
  12.     import flash.net.URLRequest;   
  13.     import flash.utils.Dictionary;   
  14.   
  15.     import org.puremvc.as3.interfaces.IProxy;   
  16.     import org.puremvc.as3.patterns.proxy.Proxy;   
  17.   
  18.     public class DataProxy extends Proxy implements IProxy   
  19.     {   
  20.         public static const NAME:String                         = 'DataProxy';   
  21.   
  22.         private var indexDic:Dictionary = new Dictionary();   
  23.   
  24.         public function DataProxy()   
  25.         {   
  26.             super( NAME, new DataVO() );   
  27.         }   
  28.   
  29.         public function urlsDataGet():void  
  30.         {   
  31.             var request:URLRequest = new URLRequest();   
  32.             var loader:URLLoader = new URLLoader();   
  33.   
  34.             sendNotification( ProgressView.SHOW );   
  35.   
  36.             request.url = vo.dataURL;   
  37.   
  38.             loader.addEventListener( ProgressEvent.PROGRESS, handleProgress );             
  39.             loader.addEventListener( Event.COMPLETE, handleURLsDataGetComplete );   
  40.   
  41.             loader.load( request );   
  42.         }   
  43.   
  44.         private function handleURLsDataGetComplete(e:Event):void  
  45.         {   
  46.             var data:XML = new XML( e.target.data );   
  47.   
  48.             for each ( var url:XML in data..url )   
  49.             {   
  50.                 vo.urlsArray.push( url.toString() );   
  51.                 vo.urlsDataArray.push( '' );   
  52.             }   
  53.   
  54.             sendNotification( URLsView.DATA_READY );   
  55.         }   
  56.   
  57.         public function imagesDataGet(index:Number):void  
  58.         {   
  59.             var request:URLRequest = new URLRequest();   
  60.             var loader:URLLoader = new URLLoader();   
  61.   
  62.             sendNotification( ProgressView.SHOW );   
  63.   
  64.             if ( vo.urlsDataArray[ index ] == '' )   
  65.             {   
  66.                 request.url = vo.urlsArray[ index ];   
  67.   
  68.                 indexDic[ loader ] = index;   
  69.   
  70.                 loader.addEventListener( ProgressEvent.PROGRESS, handleProgress );             
  71.                 loader.addEventListener( Event.COMPLETE, handleImagesDataGetComplete );   
  72.   
  73.                 loader.load( request );   
  74.             }   
  75.             else  
  76.             {   
  77.                 handleImagesDataGetFinished( index );   
  78.             }   
  79.         }   
  80.   
  81.         private function handleImagesDataGetComplete(e:Event):void  
  82.         {   
  83.             var data:XML = new XML( e.target.data );   
  84.   
  85.             vo.urlsDataArray[ indexDic[ e.target ] ] = data;   
  86.   
  87.             handleImagesDataGetFinished( indexDic[ e.target ] );   
  88.         }   
  89.   
  90.         private function handleImagesDataGetFinished(index:Number):void  
  91.         {   
  92.             sendNotification( ImagesView.DATA_READY, { index: index } );   
  93.         }   
  94.   
  95.         private function handleProgress(e:ProgressEvent):void  
  96.         {   
  97.             sendNotification( ProgressView.UPDATE, { percent: Math.round( ( e.bytesLoaded / e.bytesTotal ) * 100 ) } );   
  98.         }   
  99.   
  100.         public function get vo():DataVO   
  101.         {   
  102.             return data as DataVO;   
  103.         }   
  104.     }   
  105. }   
		package com.flashtuts.model
		{
			import com.flashtuts.model.vo.DataVO;
			import com.flashtuts.view.component.ImagesView;
			import com.flashtuts.view.component.ProgressView;
			import com.flashtuts.view.component.URLsView;

			import flash.display.LoaderInfo;
			import flash.events.Event;
			import flash.events.ProgressEvent;
			import flash.net.URLLoader;
			import flash.net.URLRequest;
			import flash.utils.Dictionary;

			import org.puremvc.as3.interfaces.IProxy;
			import org.puremvc.as3.patterns.proxy.Proxy;

			public class DataProxy extends Proxy implements IProxy
			{
				public static const NAME:String							= 'DataProxy';

				private var indexDic:Dictionary = new Dictionary();

				public function DataProxy()
				{
					super( NAME, new DataVO() );
				}

				public function urlsDataGet():void
				{
					var request:URLRequest = new URLRequest();
					var loader:URLLoader = new URLLoader();

					sendNotification( ProgressView.SHOW );

					request.url = vo.dataURL;

					loader.addEventListener( ProgressEvent.PROGRESS, handleProgress );			
					loader.addEventListener( Event.COMPLETE, handleURLsDataGetComplete );

					loader.load( request );
				}

				private function handleURLsDataGetComplete(e:Event):void
				{
					var data:XML = new XML( e.target.data );

					for each ( var url:XML in data..url )
					{
						vo.urlsArray.push( url.toString() );
						vo.urlsDataArray.push( '' );
					}

					sendNotification( URLsView.DATA_READY );
				}

				public function imagesDataGet(index:Number):void
				{
					var request:URLRequest = new URLRequest();
					var loader:URLLoader = new URLLoader();

					sendNotification( ProgressView.SHOW );

					if ( vo.urlsDataArray[ index ] == '' )
					{
						request.url = vo.urlsArray[ index ];

						indexDic[ loader ] = index;

						loader.addEventListener( ProgressEvent.PROGRESS, handleProgress );			
						loader.addEventListener( Event.COMPLETE, handleImagesDataGetComplete );

						loader.load( request );
					}
					else
					{
						handleImagesDataGetFinished( index );
					}
				}

				private function handleImagesDataGetComplete(e:Event):void
				{
					var data:XML = new XML( e.target.data );

					vo.urlsDataArray[ indexDic[ e.target ] ] = data;

					handleImagesDataGetFinished( indexDic[ e.target ] );
				}

				private function handleImagesDataGetFinished(index:Number):void
				{
					sendNotification( ImagesView.DATA_READY, { index: index } );
				}

				private function handleProgress(e:ProgressEvent):void
				{
					sendNotification( ProgressView.UPDATE, { percent: Math.round( ( e.bytesLoaded / e.bytesTotal ) * 100 ) } );
				}

				public function get vo():DataVO
				{
					return data as DataVO;
				}
			}
		}
	

You'll notice I've performed a bit of Actionscript wizardry in the "imagesDataGet()" function. Again, it's just loading some XML, nothing special about that, but it'll only load unique data once. This is the beauty of having VOs and using indexes when buttons are pressed. Essentially what happens is that if the user presses button 2 (the 3rd one), it'll check to see if that index has any data bound to it (as the loop when the urls were first loaded created an array with empty strings). If it has, there's no need to load the data again, otherwise the data's loaded and using the beauty of Dictionaries, we're able to put it into the array.

Finally, this proxy then pushes the notification "ImagesView.DATA_READY" back to our application and to our view where some magic will happen..

You've done it!

There's your first PureMVC application! You should know have an understanding of how PureMVC works and, most valuably, a skeleton so when it comes to developing more applications you can take this code, rework it or just use it as reference.

Footnote

PureMVC is a great tool and I use it on many projects, but there are some key differences between using it with Actionscript 3.0 and MXML. When you use MXML, you add the views using XML, like so:

  1. <?xml version="1.0" encoding="utf-8"?>   
  2. <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="100%" height="100%" backgroundColor="#000000" backgroundAlpha=".6"  
  3.     creationComplete="ApplicationFacade.getInstance().startup( NAME, this )"    
  4.     xmlns:component="com.application.view.component.*">   
  5.   
  6.     <mx:Script>   
  7.         <![CDATA[   
  8.   
  9.             import com.application.ApplicationFacade;   
  10.   
  11.             public static const NAME:String                     = 'Showcase';   
  12.   
  13.             public static const STACK_PROGRESS:Number           = 0;   
  14.             public static const STACK_MAIN:Number               = 1;   
  15.   
  16.         ]]>   
  17.     </mx:Script>   
  18.   
  19.     <mx:Style source="Showcase.css" />   
  20.   
  21.     <mx:ViewStack id="stack" width="980" height="750" horizontalCenter="0" verticalCenter="0" creationPolicy="all">   
  22.         <component:ProgressView id="progress" />   
  23.         <component:ShowcaseMainView id="main" />   
  24.   
  25.     </mx:ViewStack>   
  26.   
  27. </mx:Application>   
		<?xml version="1.0" encoding="utf-8"?>
		<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="100%" height="100%" backgroundColor="#000000" backgroundAlpha=".6"
			creationComplete="ApplicationFacade.getInstance().startup( NAME, this )" 
			xmlns:component="com.application.view.component.*">

			<mx:Script>
				<![CDATA[

					import com.application.ApplicationFacade;

					public static const NAME:String						= 'Showcase';

					public static const STACK_PROGRESS:Number			= 0;
					public static const STACK_MAIN:Number				= 1;

				]]>
			</mx:Script>

			<mx:Style source="Showcase.css" />

			<mx:ViewStack id="stack" width="980" height="750" horizontalCenter="0" verticalCenter="0" creationPolicy="all">
				<component:ProgressView id="progress" />
				<component:ShowcaseMainView id="main" />

			</mx:ViewStack>

		</mx:Application>
	

Flex automatically creates those views rather than allowing your to defer their creation. Therefore, when you're ready to dive into more PureMVC, they've created a little application that tells you how you can use deferred instantiation.

I hope you enjoyed following this tutorial, feel free to leave any questions or feedback in the comments!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值