Over 10 years of community discussion and knowledge are maintained here as a read-only archive.
Someone gave me the interesting opinion, that the Mediator are upstreamed Controllers
if(activeView!=null) { this.facade.removeMediator(activeView.id + 'Mediator'); }
The LocalStorageProxy does not report to the ApplicationProxy, thus the LocalStorage is coupled to the ApplicationMediator. I'll fix that
Actually it's fine for any mediator to know any proxy, just not the other way round. Here again, the direction of the coupling is key. If anything's truly reusable in your app it'll be the Model region; the proxies.
But I do not understand, why this is a better approach then mine "all proxies report to a major proxy":
In my Examlpe I use a LocalStorageProxy. If in the future, the Datasource would change to an XML, I'd have to adjust the Mediator.
package com.me.myapp.view{ import com.me.myapp.model.vo.InfoVO; import flash.events.IEventDispatcher; import spark.components.View; public interface IMyMobileApp extends IEventDispatcher { function getActiveView():View; function showLoginView():void; function showListView():void; function showInfoView( infoVO:InfoVO ):void; function showFormView():void; }}
<?xml version="1.0" encoding="utf-8"?><!-- FLEX MOBILE APPLICATION--><s:ViewNavigatorApplication firstView="com.me.myapp.view.component.LoginView" implements="com.me.myapp.view.IMyMobileApp" applicationComplete="facade.startup(this)" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:fx="http://ns.adobe.com/mxml/2009"> <fx:Script> <![CDATA[ import com.me.myapp.ApplicationFacade; import com.me.myapp.model.vo.InfoVO; import com.me.myapp.view.component.InfoView; import com.me.myapp.view.component.ListView; import com.me.myapp.view.component.LoginView; import com.me.myapp.view.component.FormView; import spark.components.View; private var facade:ApplicationFacade = ApplicationFacade.getInstance(); public static const LOGIN:String = "LoginView"; public static const LIST:String = "ListView"; public static const FORM:String = "FormView"; public static const INFO:String = "InfoView"; // Read-only context private function get context():String { // if there is no context, then we are on the LoginView, // set by firstView (we can't set a context for firstView) return (navigator.context)?String(navigator.context):LOGIN; } //-------------------------------------------------------------------------- // Below methods satisfy IMyMobileApp interface //-------------------------------------------------------------------------- // GET THE ACTIVE VIEW public function getActiveView():View { // Since the LoginView is specified as the 'firstView' // we need to mediate it in the traditional way, which means // we need a way to get the active view from the mediator return navigator.activeView; } // SHOW THE LOGIN VIEW public function showLoginView( ):void { // Only return to LoginView if we're not already on it if ( context != LOGIN ) navigator.popToFirstView(); } // SHOW THE LIST VIEW public function showListView( ):void { // Only act if we're not already on the ListView if ( context != LIST ) { // If returning (from Info or Form views), just pop // otherwise push a new ListView var returning:Boolean = ( context == INFO || context == FORM ); if ( returning ) { navigator.popView(); } else { navigator.pushView( ListView, null, LIST ); } } } // SHOW THE FORM VIEW public function showFormView( ):void { // Only push a FormView if we're not already on it if ( context != FORM ) navigator.pushView( FormView, null, FORM ); } // SHOW THE INFO VIEW (PASSING IN INFO VO) public function showInfoView( infoVO:InfoVO ):void { // Only push an InfoView if we're not already on it // Pass in the InfoVO from the note body if ( context != INFO ) navigator.pushView( InfoView, infoVO, INFO ); } ]]> </fx:Script> </s:ViewNavigatorApplication>
package com.me.myapp{ import com.me.myapp.controller.command.StartupCommand; import com.me.myapp.controller.constants.AppConstants; import com.me.myapp.view.IMyMobileApp; public class ApplicationFacade { public static function getInstance() : ApplicationFacade { if ( instance == null ) { instance = new ApplicationFacade(); } return ApplicationFacade( instance ); } public function startup( app:IMyMobileApp ):void { registerCommand( AppConstants.STARTUP, StartupCommand ); sendNotification( ViewerConstants.STARTUP, app ); } }}
package com.me.myapp.controller.command{v import com.me.myapp.model.proxy.InfoProxy; import com.me.myapp.model.proxy.ThingProxy; import com.me.myapp.view.IMyMobileApp; import com.me.myapp.view.mediator.ApplicationMediator; import org.puremvc.as3.interfaces.INotification; import org.puremvc.as3.patterns.command.SimpleCommand; public class StartupCommand extends SimpleCommand { override public function execute( note : INotification ) : void { // Prepare the Controller facade.registerCommand( AppConstants.MEDIATE_VIEW, MediateViewCommand ); // Prepare the Model facade.registerProxy( new ThingProxy( ) ); facade.registerProxy( new InfoProxy( ) ); // Prepare the View var app:IMyMobileApp = IMobileViewer( note.getBody() ); facade.registerMediator( new ApplicationMediator( app ) ); } }}
package com.me.myapp.view.mediator{ import com.me.myapp.controller.constants.AppConstants; import com.me.myapp.view.IMyMobileApp; import com.me.myapp.model.InfoVO; import flash.events.Event; import org.puremvc.as3.interfaces.INotification; import org.puremvc.as3.patterns.mediator.Mediator; import spark.components.View; public class ApplicationMediator extends Mediator { public static const NAME:String = "ApplicationMediator"; // CONSTRUCTOR - HANDLE COMPONENT BY ITS INTERFACE public function ApplicationMediator( app:IMyMobileApp ) { super( NAME, app ); } // CALLED AT REGISTRATION - LIST NOTIFICATION INTERESTS override public function listNotificationInterests():Array { return [ AppConstants.SHOW_LOGIN, AppConstants.SHOW_LIST, AppConstants.SHOW_FORM, AppConstants.SHOW_INFO, ]; } // CALLED AT REGISTRATION - LISTEN FOR EVENTS, MEDIATE FIRST VIEW override public function onRegister():void { // Listen for Views to be added to the ViewNavigatorApplication app.addEventListener( Event.ADDED, handleAddedEvent ); // Mediate the firstView (currently the LoginView) sendNotification( AppConstants.MEDIATE_VIEW, app.getActiveView() ); } // HANDLE NOTIFICATIONS TO CHANGE VIEWS override public function handleNotification( note:INotification ):void { switch ( note.getName() ) { case AppConstants.SHOW_LOGIN: app.showLoginView(); break; case AppConstants.SHOW_LIST: app.showListView(); break; case AppConstants.SHOW_FORM: app.showFormView(); break; case AppConstants.SHOW_INFO: app.showInfoView( InfoVO( note.getBody() ) ); break; } } // HANDLE ADDED EVENTS (Event.ADDED) private function handleAddedEvent( event:Event ):void { if ( event.target is View ) { sendNotification( AppConstants.MEDIATE_VIEW, event.target ); } } // CAST THE VIEW COMPONENT TO THE PROPER TYPE private function get app():IMyMobileApp { return viewComponent as IMyMobileApp; } }}
package com.me.myapp.controller.command{ import com.me.myapp.view.component.InfoView; import com.me.myapp.view.component.ListView; import com.me.myapp.view.component.LoginView; import com.me.myapp.view.component.FormView; import com.me.myapp.view.mediator.InfoViewMediator; import com.me.myapp.view.mediator.ListViewMediator; import com.me.myapp.view.mediator.LoginViewMediator; import com.me.myapp.view.mediator.FormViewMediator; import org.puremvc.as3.interfaces.INotification; import org.puremvc.as3.interfaces.IMediator; import org.puremvc.as3.patterns.command.SimpleCommand; import spark.components.View; public class MediateViewCommand extends SimpleCommand { /** * Each View is destroyed once it is moved away from, * thus Mediators must also be transient. Any existing * Mediator is removed, and a new one registered * along with the new View. Any additional mediators * associated with the children of these Views should be * registered and removed the onRegister() and onRemove() * methods of the View's Mediator. */ override public function execute( note:INotification ):void { var view:View = View( note.getBody() ); var mediator:IMediator; switch ( view.className ) { case "LoginView": facade.removeMediator( LoginViewMediator.NAME ); mediator = new LoginViewMediator( view as LoginView ); break; case "ListView": facade.removeMediator( ListViewMediator.NAME ); mediator = new ListViewMediator( view as ListView ); break; case "FormView": facade.removeMediator( FormViewMediator.NAME ); mediator = new FormViewMediator( view as FormView ); break; case "InfoView": facade.removeMediator( InfoViewMediator.NAME ); fmediator = new InfoViewMediator( view as InfoView ); break; } if (mediator) facade.registerMediator( mediator ); } }}
<?xml version="1.0" encoding="utf-8"?><!-- LIST VIEW --><s:View xmlns:s="library://ns.adobe.com/flex/spark" xmlns:fx="http://ns.adobe.com/mxml/2009" destructionPolicy="never"> <!-- LIST STUFF HAPPENS HERE --> </s:View>
Remove mediators when a view is removed.
// CALLED AT REGISTRATION - LISTEN FOR EVENTS, MEDIATE FIRST VIEW override public function onRegister():void { // Listen for Views to be added to the ViewNavigatorApplication app.addEventListener( Event.ADDED, handleAddedEvent ); // Mediate the firstView (currently the LoginView) sendNotification( AppConstants.MEDIATE_VIEW, app.getActiveView() ); }
// HANDLE ADDED EVENTS (Event.ADDED) private function handleAddedEvent( event:Event ):void { if ( event.target is View ) { sendNotification( AppConstants.MEDIATE_VIEW, event.target ); } }
if you are mediating a view, lets say you default first view; LoginView, we shouldn't directly access the MyMobileApp in order to call these methods so what is the approach here?
// Mediate the firstView (currently the LoginView) sendNotification( AppConstants.MEDIATE_VIEW, app.getActiveView() );