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]
Print
Author Topic: Having difficulties changing views  (Read 13392 times)
danielcsgomes
Full Member
***
Posts: 47

 - daniel@onedesign.com.pt
View Profile Email
« on: August 13, 2008, 03:01:33 »

Hey guys, i am newbie to PureMVC and Design Patterns, and i have a few questions to make.

I just started to see some demos, like the login, employee admin, cafetownsend and i am trying to make something like cafetownsend mixed with employee admin but with weborb getting data from database with CRUD operations.

I already have the login working getting the crediantials from database, now i am with some difficulties to figured out how do i change the view when i logged in to the User Management Panel.

my file struture is this:


In other demos i saw that exists an ApplicationMediator, ApplicationProxy, ApplicationCommand but i dont really undertood it yet, but i thought its the way to solve my problem. Because what is make me confuse is change views/components from different mediators and proxys.

Sorry my bad english  :-\

Thanks for your time,

Daniel Gomes
« Last Edit: August 13, 2008, 06:07:03 by danielcsgomes » Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #1 on: August 13, 2008, 08:35:00 »

In other apps, you'll certainly see ApplicationMediator often. This is usually the first (and sometimes only) Mediator created, used to mediate communications with the main app, be it  Flash movie or a Flex Application, etc.

I see you have only a UserManagementMediator (for your UserManagementContainer) and a LoginMediator for your LoginPanel.

I'm assuming your main app does something like having a ViewStack with the LoginPanel and the UserManagementContainer as children. The implementation may be different, but am I safe in assuming the main app somehow wants to switch between these 2 children?

So you want to create an ApplicationMediator and register it to mediate your main app. Have the LoginProxy send a notification that the ApplicationMediator hears that causes it to tell the main app to flip over to the UserManagementContainer.

The UserManagementContainer will probably not exist yet if it is second  child of a ViewStack, so you'll want the ApplicationMediator to also register the UserManagementMediator, passing a reference to the UserManagementContainer once you've had the main app display it.

-=Cliff>

Logged
danielcsgomes
Full Member
***
Posts: 47

 - daniel@onedesign.com.pt
View Profile Email
« Reply #2 on: August 13, 2008, 08:50:53 »

Hi Cliff,

Yes you are assuming right, i have a ViewStack with LoginPanel and UserManagementContainer as a child and the objectivo is switch between them.

Now i understand a little more the "concept" of ApplicationMediator, but i have another question maybe a stupid one, so and if i dont have a ViewStack in main app, how the ApplicationMediator remove/add views from main app?


Thanks for your reply and explanation,

Daniel Gomes

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



View Profile WWW Email
« Reply #3 on: August 13, 2008, 09:19:44 »

One way is to coordinate the removal of the first component and the manufacture and addition of the second from a Command. Lets say your LoginProxy has sent a Notification that the login is good, and the note contains a reference to a UserVO with user details. Inside the ManageUserCommand, triggered by this note:

:
override public function execute(note:INotification):void
{
    var userVO:UserVO = note.getBody() as UserVO;
    var userMgmtContainer:UserManagementContainer = new UserManagementContainer();
    userMgmtContainer.id = 'userMgmtContainer';
    userMgmtContainer.userVO = userVO;
    sendNotification( ApplicationFacade.REMOVE_LOGIN_PANEL );
    sendNotification( ApplicationFacade.ADD_USER_MGMT_CTNR, userMgmtContainer );
}

Inside the ApplicationMediator:

:

override public function listNotificationInterests():Array
{
    return [ ApplicationFacade.REMOVE_LOGIN_PANEL,
             ApplicationFacade.ADD_USER_MGMT_CTNR,
             ... ]
}

override public function handleNotification( note:INotification ):void
{

   case ApplicationFacade.REMOVE_LOGIN_PANEL:
        app.removeChild(loginPanel);
        break;

   case ApplicationFacade.ADD_USER_MGMT_CTNR:
        app.addChild(note.getBody() as UserManagementContainer);
        break;

}

private function app():MyApplication
{
   return viewComponent as MyApplication;
}

// assuming the login panel is there first and has id loginPanel.
private function loginPanel():LoginPanel
{
   return app.loginPanel as LoginPanel;
}


-=Cliff>
Logged
danielcsgomes
Full Member
***
Posts: 47

 - daniel@onedesign.com.pt
View Profile Email
« Reply #4 on: August 13, 2008, 09:30:58 »

Now it starts to make sense to me :)

Thanks for the explanation again Cliff, it was very usefull. Now i will try implement it!

Daniel Gomes



Logged
danielcsgomes
Full Member
***
Posts: 47

 - daniel@onedesign.com.pt
View Profile Email
« Reply #5 on: August 13, 2008, 04:01:04 »

Have good news, for now it removes the Login Panel and add the Main App Painel (i changed some names and organized better the components).

But i still have some problems, because i only can remove the Login Panel with "app.removeChildAt(0)" if i tried with "app.removeChild(loginPanel)" it give a error :S and to add the Main App Painel i have to create a var and create a new MainApp. (see below in code)

This is my ApplicationMediator.as
:
//Define Proxy
private var _loginProxy : LoginProxy;
private var _userProxy : UserProxy;

//Define static const for mediator
public static const NAME:String = "ApplicationMediator";

public function ApplicationMediator( viewComponent: Login ):void
{
super ( NAME, viewComponent );

// local reference to the LoginProxy
_loginProxy = facade.retrieveProxy(LoginProxy.NAME) as LoginProxy;
_userProxy = facade.retrieveProxy(UserProxy.NAME) as UserProxy;

facade.registerMediator(new LoginPanelMediator(app.loginPanel));
}

override public function listNotificationInterests():Array
{
    return [   ApplicationFacade.LOGIN_SUCCESS,
    ApplicationFacade.LOGIN_FAILED,
            ]
}

override public function handleNotification( note:INotification ):void
{
switch ( note.getName() )
{
   case ApplicationFacade.LOGIN_FAILED:

break;

   case ApplicationFacade.LOGIN_SUCCESS:
    app.appView.removeChildAt(0);
    var mainAppPanel: MainApp = new MainApp();
    app.addChild(mainAppPanel);
        break;
}
}

private function get app(): Login
{
   return viewComponent as Login;
}

// assuming the login panel is there first and has id loginPanel.
private function get loginPanel(): Login
{
   return app.loginPanel as Login;
}

My MainAppCommand.as
:
/**
* Listens to the login notification retrieving the LoginProxy and does login
*/
override public function execute(note:INotification):void
{
    var userVO:UserVO = note.getBody() as UserVO;
    var mainApp:MainApp = new MainApp();
    mainApp.id = 'mainAppPanel';
    sendNotification( ApplicationFacade.LOGIN_SUCCESS );
    sendNotification( ApplicationFacade.ADD_MAIN_APP );
}


My ApplicationFacade.as
:
//Notifications const names
public static const STARTUP_APP:String = "startupApp";

//Login const names
public static const LOGIN:String = "login";
public static const LOGIN_SUCCESS:String  = "loginSuccess";
public static const LOGIN_FAILED:String  = "loginFailed";

//Main const names
public static const MAIN_APP:String  = "mainApp";
public static const ADD_MAIN_APP:String  = "addMainApp";


/**
* Singleton ApplicationFacade Factory Method
*/
public static function getInstance() : ApplicationFacade
{
if( instance == null) instance = new ApplicationFacade();
return instance as ApplicationFacade;
}

/**
* Start the application
*/
public function startupApp(app:Login):void
{
sendNotification(STARTUP_APP, app);
}

/**
* Register Commands with the Controller
*/
override protected function initializeController( ) : void
{
super.initializeController();
registerCommand( STARTUP_APP, StartupAppCommand );
registerCommand( LOGIN, LoginCommand );
}


And this is my new File Structure:


Something is missing me, just dont know where :S

UPDATE: I just upload the App you can see it here:
http://onedesign.com.pt/puremvc/Login.html
Username: admin Password: admin

Thanks for your time,

Daniel Gomes
« Last Edit: August 13, 2008, 04:23:35 by danielcsgomes » Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #6 on: August 16, 2008, 09:10:39 »

Although a reference to your Flex app will show you have app.appView and app.loginPanel as properties, from a display hierarchy standpoint, your LoginPanel is not a child of the main app but instead a child of the VBox called appView which is the only child of the main app:

:
<mx:VBox id="appView">
    <loginComp:LoginPanel id="loginPanel" />
</mx:VBox>

Your actual display hierarchy is app.appView.loginPanel. When you do a app.removeChildAt(0) you're removing appView.

This is a disparity between an MXML component which uses the 'id' property to reference all the child components it defines as direct sibling properties, regardless of their location in the display hierarchy, and Flash DisplayObject, which implements the display hierarchy and uses the 'name' property to reference children, which are not properties but array elements.

Its confusing, because app.appView.loginPanel is not valid object notation because loginPanel is not a property of appView, even though it is a child.

To test this, in your MainApp, add this:

:
<mx:Script>
<![CDATA[
public function initApp():void
{
                        // The following will fail with:
                        // ArgumentError: Error #2025: The supplied DisplayObject must be a child of the caller
                        // this.removeChild(loginPanel);

                        // this will work because the loginPanel is really a child of appView
this.appView.removeChild(loginPanel);

}
]]>
</mx:Script>


and in the Application tag add:

:
creationComplete="initApp()" and comment out your facade.startup for now.

Run this and you'll see your loginPanel disappear.

One side note here: You plan to add other component definitions to your main application, but that nested VBox is not needed in this current implementation, and in fact slows the performance of your Flex app because it adds an unnecessary sizing and layout pass. You can instead just set your Application to  layout="vertical". Never make your first and only container within an Application be a Canvas, HBox or VBox, because the Application is all of these.

So, given what we know now about your display hierarchy, and assuming you leave the appView VBox where it is the following change to the mediator should work:

:
override public function handleNotification( note:INotification ):void
{

   case ApplicationFacade.REMOVE_LOGIN_PANEL:
        app.appView.removeChild(loginPanel);
        break;

   case ApplicationFacade.ADD_USER_MGMT_CTNR:
        app.appView.addChild(note.getBody() as UserManagementContainer);
        break;

}

-=Cliff>
Logged
danielcsgomes
Full Member
***
Posts: 47

 - daniel@onedesign.com.pt
View Profile Email
« Reply #7 on: August 16, 2008, 12:58:39 »

It works perfect now :D

Thanks Cliff!

Daniel Gomes
Logged
Pages: [1]
Print