Futurescale, Inc. PureMVC Home

The PureMVC Framework Code at the Speed of Thought


Over 10 years of community discussion and knowledge are maintained here as a read-only archive.

New discussions should be taken up in issues on the appropriate projects at https://github.com/PureMVC

Pages: [1] 2 3
Print
Author Topic: ViewStacks, Mediators and Deferred Instantiation  (Read 55726 times)
shizny
Full Member
***
Posts: 31


View Profile Email
« on: February 29, 2008, 09:31:54 »

I've been looking through the cafe townsend demo.  I have a question.  I'm developing an app with multiple 'pages'.  It looks like the author of the project used the file ApplicationMediator to switch between view stacks.  Is this the preferred way to switch between views? 

Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #1 on: February 29, 2008, 10:33:11 »

Generally, I like to expose a variable in the component that contains the ViewStack, and bind the stack's selectedIndex to it. Then whichever Mediator manages that component sets the property, causing the stack's selectedIndex to change without having to know that there's a ViewStack inside the component it's mediating.

Also, in that component, I like to expose public static constants that give names to the indices of the ViewStack, so that the Mediator also doesn't need to know the actual index numbers to use. The constants and the property to set or check become part of the API the component exposes to its Mediator.

-=Cliff>
Logged
shizny
Full Member
***
Posts: 31


View Profile Email
« Reply #2 on: February 29, 2008, 01:25:16 »

Thanks for the info.  I'm a little confused, and this stuff is still pretty tough for me.  Do any of the demos work in the manner you are discussing, or do you have any other examples? 
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #3 on: February 29, 2008, 08:35:17 »

Look at the architecture 101 demo. Examine the view components and their mediators. They're not switching viewstacks but populating forms and lists and grids. Same concept though. The view component exposes a public var for UserVO, which the mediator sets or gets. The grid or list or form fields bind to that public var and get updated when it changes.

-=Cliff>
Logged
shizny
Full Member
***
Posts: 31


View Profile Email
« Reply #4 on: March 02, 2008, 04:50:57 »

Starting to make more sense now.  I'm still not there but I had a flash of thinking I knew what was going on, and that was cool.  I have another question that relates to this.  I've been reading about deferring view instantiation with PureMVC on a few forums.  I've been creating each 'page' of my app as a module and was going to load them when needed.  Am I going to have to do this deferred instaniation stuff?  Is this a good idea considering I'm developing an AIR app?

Thanks in advance.
Josh
Logged
shizny
Full Member
***
Posts: 31


View Profile Email
« Reply #5 on: March 03, 2008, 10:58:33 »

When you say

"Generally, I like to expose a variable in the component that contains the ViewStack"

are you saying that I should have a component just for the viewStack, kinda like an empty movieclip in flash that I would load other movieclips into?  Do I even need to use a viewstack?  Should I be using modules for this?

Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #6 on: March 03, 2008, 12:49:35 »

Here's an example of a custom ViewStack component that exposes a simple API of a property and some constants for its valid settings.

Note that it binds its selectedIndex property to this 'currentViewSelector' property, which is initialized to the value for the LoginView child and can be manipulated by the Mediator for this component by setting it to one of the constant values.

MainDisplay.mxml -Take 1

:
<?xml version="1.0" encoding="utf-8"?>
<mx:ViewStack xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:view="com.me.myapp.view.components.*"
    selectedIndex="{currentView}">
   
    <mx:Script>
        <![CDATA[

            public static const GALLERY:String = 0;
            public static const EDITOR:String = 1;
            public static const PROFILE:String = 2;

            [Bindable] public var currentViewSelector:String = GALLERY;
           
        ]]>
    </mx:Script>

    <view:GalleryView/>
    <view:EditorView/>
    <view:ProfileView/>

</mx:ViewStack>


Why not just have the Mediator poke the value straight into the custom ViewStack component's selectedIndex property?

It'd expose too much of the internals of the component to assume that it is an ordinal that is being set and that it is in fact a ViewStack that is being managed.

Later you may wish to wrap this ViewStack and a group of buttons for controlling it into a single custom component extending, say VBox, rather than have the buttons remain separate and have their own Mediator.

So this Mediator now has a VBox for a child and that has no selectedIndex. But it retains a currentViewSelector property, so the Mediator isn't affected on that point.

MainDisplay.mxml - Take 2.

:
<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:view="com.me.myapp.view.components.*"
    >

    <mx:MetaData>
        [Event('showGallery')]
        [Event('showProfile')]
        [Event('showEditor')]
    </mx:MetaData>
   
    <mx:Script>
        <![CDATA[
            public static const GALLERY:String = 0;
            public static const EDITOR:String = 1;
            public static const PROFILE:String = 2;

            public static const SHOW_GALLERY:String = 'showGallery';
            public static const SHOW_EDITOR:String = 'showEditor';
            public static const SHOW_PROFILE:String = 'showProfile';

            [Bindable] public var currentViewSelector:String = GALLERY;
            public var activeView:Object;
        ]]>
    </mx:Script>
   
    <mx:Binding source="myStack.selectedChild" destination="activeView"/>

    <mx:ViewStack id="myStack" selectedIndex="{currentViewSelector}">
       <view:GalleryView/>
       <view:EditorView/>
       <view:ProfileView/>
    </mx:ViewStack>
   
    <mx:HBox>

       <mx:Button label="Gallery"
           enabled="{myStack.selectedIndex != GALLERY}"
           click="dispatchEvent(new Event(SHOW_GALLERY, true))"/>
       
     <mx:Button label="Editor"
           enabled="{myStack.selectedIndex != EDITOR}"
           click="dispatchEvent(new Event(SHOW_EDITOR, true))"/>

       <mx:Button label="Profile"
           enabled="{myStack.selectedIndex != PROFILE}"
           click="dispatchEvent(new Event(SHOW_PROFILE, true))"/>

   </mx:HBox>

</mx:VBox>


The Mediator still sets the currentView property to affect the displayed view, even though the component implementation is completely different. The ViewStack still binds to this value for its selectedIndex.

So lets think about what this Mediator might look like and do. It'd be silly to have it be there for no other purpose than to listen to the buttons and switch the view. If that were the case, then we'd encapsulate it all inside the custom component, having the buttons directly  change the ViewStack child.

However, perhaps we need the rest of our application to know that we have switched the displayed child. Perhaps each one has a different background color associated and the ApplicationMediator listens for the change and changes its background color. Or whatever.

So we know at a minimum we'll listen for the button click events and set the appropriate value for currentViewSelector, and we'll send a Notification to the rest of the system.

But while we're at it, lets not skirt the big issue here:

Unless that ViewStack has creationPolicy='all' (not recommended) and you've elsewhere added all the Mediators for the children, you've got a problem. Deferred instantiation behavior of a ViewStack means that the first child is created, but the others don't exist until we navigate to them.

The MainDisplayMediator gets the sole reference MainDisplay as its View Component so lets put it in charge of the problem of making sure the children of its View Component get the appropriate Mediators created and registered for them as soon as they are created.

We know that the first one will be created right away. So in our constructor, we can create the mediator for it.

Then, whenever we get button presses that toggle the current view, we set the currentViewSelector, check for and create a mediator for the new child if necessary, and then send a notification to the rest of the system that that view has been selected. It is important to check for and create the mediator first before sending the Notification, because other actors in the system will probably want to interact with it as a result of the Notification.

Note that in the MainDisplay.as Take 2, we added another variable 'activeView' and bind the currentChild of the viewStack to it. That will be used to reach the new child we'll be creating a Mediator for.

MainDisplayMediator.as - (NOTE: This is a PureMVC 2.0 style Mediator, the slight differences are org.puremvc.as3 package, sending NAME to the constructor and not having to override getMediatorName)

:
package com.me.myapp.view
{
        import com.me.myapp.view.components.MainDisplay;
import org.puremvc.as3.interfaces.INotification;
import org.puremvc.as3.patterns.mediator.Mediator;

public class MainDisplayMediator extends Mediator
{
public static const NAME:String = 'MainDisplayMediator';

public function MainDisplayMediator( viewComponent:MainDisplay )
{
super( NAME, viewComponent );

                        mainDisplay.addEventListener( MainDisplay.SHOW_GALLERY, onShowGallery );
                        mainDisplay.addEventListener( MainDisplay.SHOW_EDITOR, onShowEditor  );
                        mainDisplay.addEventListener( MainDisplay.SHOW_PROFILE, onShowProfile );
                        checkForMediator(  MainDisplay.currentViewSelector, mainDisplay.activeView );
}

protected function get MainDisplay():MainDisplay
{
return viewComponent as MainDisplay;
}

protected function onShowEditor(event:Event):void
{
mainDisplay.currentViewSelector = MainDisplay.EDITOR;
                        checkForMediator(  MainDisplay.EDITOR, mainDisplay.activeView );
                        sendNotification( ApplicationFacade.EDITOR_MODE );
}

protected function showGallery(event:Event ):void
{
mainDisplay.currentViewSelector= MainDisplay.GALLERY;
                        checkForMediator(  MainDisplay.GALLERY, mainDisplay.activeView );
                        sendNotification( ApplicationFacade.GALLERY_MODE );
}
 
protected function showProfile(event:Event ):void
{
mainDisplay.currentViewSelector = MainDisplay.PROFILE;
                        checkForMediator(  MainDisplay.PROFILE, mainDisplay.activeView );
                        sendNotification( ApplicationFacade.PROFILE_MODE );
}

                protected function checkForMediator( childSelector:String, child:Object ):void
                {
                        switch (childSelector)
                        {
                             case MainDisplay.PROFILE:
                                  if ( retrieveMediator( ProfileViewMediator.NAME ) == null )
                                     facade.registerMediator(new ProfileViewMediator( child ));
                                  break;
                             case MainDisplay.GALLERY:
                                  if ( retrieveMediator( GalleryViewMediator.NAME ) == null )
                                     facade.registerMediator(new GalleryViewMediator( child ));
                                  break;
                             case MainDisplay.EDITOR:
                                  if ( retrieveMediator( EditorViewMediator.NAME ) == null )
                                     facade.registerMediator(new EditorViewMediator( child ));
                                  break;
                        }
         
                }

}
}
« Last Edit: August 16, 2008, 02:58:18 by puremvc » Logged
shizny
Full Member
***
Posts: 31


View Profile Email
« Reply #7 on: March 05, 2008, 10:28:58 »

Wow!  Thanks for all the info.  I'm trying to digest it, and get this stuff straight.  Probably be back with a few questions.

Thanks again,
Josh
Logged
shizny
Full Member
***
Posts: 31


View Profile Email
« Reply #8 on: March 05, 2008, 08:06:35 »

Not to switch gears here, but do I even need a viewstack, or should I just adjust my components visible values in the mediators.  If I just register my components to listen for the correct events I should be able to handle everything in the mediator right?  Is that a bad way to do it.  Sorry for all these questions, but this is really new to me.   
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #9 on: March 05, 2008, 09:05:26 »

You could do that but then you'd be burdening your Mediator with the logic that the ViewStack already handles. If you're in Flash, or a platform with no ViewStack type inmplementation, then I suppose you'll have to resort to something.

But there's no sense rebuilding that wheel if you're using Flex or AIR. It gives you deferred instantiation, which is a good thing. The startup time of your app is critical, and object creation overhead has its impact. If you can defer the creation of the children not shown, then your app comes up quicker.

You could of course handle deferred instantiation in the Mediator as well, but again that would be over-complicating the Mediator. The more appropriate place would be a Command, but that's pushing it deeper into the framework and causing a new class to deal with it.

-=Cliff>
Logged
shizny
Full Member
***
Posts: 31


View Profile Email
« Reply #10 on: March 05, 2008, 10:19:48 »

Yeah, about commands.  I've read your documentation, read a ton of blogs about puremvc and thought I understood them until I started working with this stuff.  I don't see any reason to do any commands yet in my application (outside of startup).  Do commands make more sense when you have more than one thing to do off of an event?  Right now my stuff is real basic, pressing a button and then switching a viewstack selected index.

Also, earlier in this post you wrote
selectedIndex="{currentView}"

did you mean selectedIndex="{currentViewSelector}"

or am I super dense?
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #11 on: March 06, 2008, 06:08:59 »

Your correction is correct.

And while Commands are something that are a bit broadly used in other frameworks, they definitely have a place here in PureMVC-land.

Commands are great when the same functionality needs to be accessed from multiple places in the view. For instance, a PasteCommand might retrieve the ClipboardProxy and get the current clipboard object, and then retrieve the current view context (are we in a text editor, a canvas, etc) and make sure the clipboard object type is accepted by the current view context. If so, send a notification with that object as the body. The appropriate mediator responds and sets the data on its child.

That PasteCommand could be triggered from a keystroke or a menu click, heard by different mediators. They would each send the PASTE Notification, rather than do than duplicate that work in two mediators.

-=Cliff>
Logged
shizny
Full Member
***
Posts: 31


View Profile Email
« Reply #12 on: March 06, 2008, 09:04:41 »

Thanks again.
Logged
shizny
Full Member
***
Posts: 31


View Profile Email
« Reply #13 on: March 06, 2008, 09:13:23 »

Oh man, I think I just understood your mainDisplay take 1 take 2 Post!!  That is exciting for me:>   
Logged
makar
Newbie
*
Posts: 3


View Profile Email
« Reply #14 on: March 10, 2008, 02:00:40 »

Hi!

was happy to find this topic, as my application is nearly only viewStack based components inside eachothers... but it doen't work.
I'm not new with MVC+C (AS2 pixlib addict :) but Flex and PureMVC are new for me.

can somebody tell me where I'm wrong?

main.mxml has :
     2 perso very simple components (for navigation, so contains only buttons)
     one viewStack, holding either a Home.mxml component or a antoher view
     and cliff'code with activeView and currentView, etc...


I've put all the habitual staff with Startup Notification : this works

the startup command is a macrocommand, calling ModelPrep and then ViewPrepCommand... this works also

the viewPrep Command registers my ApplicationMediator :
     the constructor has this
:
super(NAME,viewComponent);
trace("app : "+app);  // traces the get method for giving back casted viewcomponent
trace("app nav : "+app.toolBar);   // traces the first simple navigation component
trace("currentView : "+app.currentViewSelector);
trace("activeView : "+app.activeView);
checkForMediator(app.currentViewSelector,app.activeView);

the result is:
app : Main
app toolbar : Main.toolBar
currentView : 0
activeView : null


I always get a null has main viewstack item but it's displayed on the screen...
so when is it created, why do I receive a null ??

please help
thx

Makar

Logged
Pages: [1] 2 3
Print