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: ViewStack: Change view but dont load view components  (Read 17236 times)
danielcsgomes
Full Member
***
Posts: 47

 - daniel@onedesign.com.pt
View Profile Email
« on: August 16, 2008, 12:28:25 »

Hi guys,

I am having some difficulties loading views components in viewstack.
See the steps:

- MainApp load
- ViewStack default view: A
- I loggin
- ViewStack Changes view to: B
- View B have more two components inside (B.1,B.2)
- When i tried send data for components B.1 and B.2 their are unavaible it sends an error that object is null

So i have doing some search and find maybe it's creationPolicy, the default is auto and i changed for all and the objects are now avaliable.

But my question/difficulty is load the components without the creationPolicy all, becaus now my App just have 2 views and 3 components, but when i have 50 components load all at same time dont make sense and make the server slow and the application, or am i wrong? What is the best practice to do this?

Thanks,

Daniel Gomes
« Last Edit: August 16, 2008, 12:57:59 by danielcsgomes » Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #1 on: August 16, 2008, 04:11:31 »

Here's a link to the newly created FAQ entry for this age-old problem - How to handle Deferred Instantiation.

http://puremvc.org/content/view/91/185/

-=Cliff>

PS: Congratulations, you stated it succinctly enough to quote it directly as the question in the FAQ!

Logged
danielcsgomes
Full Member
***
Posts: 47

 - daniel@onedesign.com.pt
View Profile Email
« Reply #2 on: August 16, 2008, 04:28:30 »

This new FAQ entry will help the some people with the same question ;) and the way that you cover and explain in, is very easy to understand.

Now i will implement this and will try to finish my first app ("User Manager Demo App").
When i finish it, i will put online maybe can help other guys that are starting with PureMVC using WebOrb|php and Mysql ;)

Daniel Gomes
« Last Edit: August 16, 2008, 04:42:16 by danielcsgomes » Logged
danielcsgomes
Full Member
***
Posts: 47

 - daniel@onedesign.com.pt
View Profile Email
« Reply #3 on: August 16, 2008, 05:21:37 »

I was trying implement it but getting null again :s

will post the code:

Main.mxml
:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
layout="horizontal"
xmlns:loginComp="com.pt.onedesign.user_manager.view.components.Login.*"
xmlns:userManagerComp="com.pt.onedesign.user_manager.view.components.UserManager.*"
initialize="facade.startupApp(this)">

<mx:Script>
<![CDATA[
import com.pt.onedesign.user_manager.view.ApplicationMediator;
import com.pt.onedesign.user_manager.ApplicationFacade;

private var facade:ApplicationFacade = ApplicationFacade.getInstance();

            [Bindable] public var currentViewSelector:uint = ApplicationMediator.LOGIN_PANEL;
            public var activeView:Object;


]]>
</mx:Script>

  <mx:Binding source="appView.selectedChild" destination="activeView"/>
<mx:ViewStack id="appView" resizeToContent="true" selectedIndex="{currentViewSelector}">
<loginComp:LoginPanel id="loginPanel" />
<userManagerComp:UserManager id="userManagerPanel"/>
</mx:ViewStack>
</mx:Application>

ApplicationMediator.as
:
package com.pt.onedesign.user_manager.view
{

import com.pt.onedesign.user_manager.model.LoginProxy;
import com.pt.onedesign.user_manager.model.UserProxy;

import mx.controls.Alert;
import mx.core.Container;

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
{
//Define Proxy
private var _loginProxy : LoginProxy;
private var _userProxy : UserProxy;

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

// Define const names for change ViewStack views.
public static const LOGIN_PANEL : Number = 0;
public static const USER_MANAGER_PANEL : Number = 1;

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

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

facade.registerMediator(new LoginPanelMediator(app.loginPanel));
//facade.registerMediator(new  UserListMediator(app.userManagerPanel.userList));
}

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

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

override public function handleNotification( note:INotification ):void
{
var child:Container;
switch ( note.getName() )
{
   case LoginProxy.LOGIN_FAILED:
Alert.show("Invalid username or password, please try it again.","Login Failed");
app.currentViewSelector = LOGIN_PANEL;
break;

   case LoginProxy.LOGIN_SUCCESS:
    app.currentViewSelector = USER_MANAGER_PANEL;
    facade.registerMediator(new UserListMediator(app.activeView.userList));
        break;
}
}


}
}

UserManager.mxml (Component that receive more 2 components)
:
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas
width="900"
xmlns:comps="com.pt.onedesign.user_manager.view.components.UserManager.components.*"
xmlns:mx="http://www.adobe.com/2006/mxml">

<mx:HBox horizontalGap="10" horizontalAlign="center" verticalAlign="top" width="100%" paddingTop="15">
<mx:VBox verticalGap="10">
<comps:UserList id="userList"/>
</mx:VBox>
<comps:UserForm id="userForm"/>
</mx:HBox>

</mx:Canvas>

It's strange, but the same thing in ApplicationMediator.as if i put the this:
:
case LoginProxy.LOGIN_SUCCESS:
app.currentViewSelector = USER_MANAGER_PANEL;
        app.activeView.creationPolicy=all; <------------ It make the other components avaible but without this they still null :|
facade.registerMediator(new UserListMediator(app.activeView.userList));
break;

I really don't understand why it don't load the components :|
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #4 on: August 17, 2008, 04:51:16 »

CreationPolicy=all has to be set in the MXML, its used at construction time to determine whether to create all children or only the visible one. Setting it afterward has no effect.

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

 - daniel@onedesign.com.pt
View Profile Email
« Reply #5 on: August 17, 2008, 05:22:19 »

Humm, i think i know what is happen, because when the notification is send of LOGIN_SUCCESS the UserList Mediator will try to fill datagrid with data and at this time the Mediador isn't created and the instance so i think i have to send another notification like: GET_USERS_LIST after switch the views and add the UserListMediator, i will try and send a feedback of it.

Daniel Gomes
Logged
danielcsgomes
Full Member
***
Posts: 47

 - daniel@onedesign.com.pt
View Profile Email
« Reply #6 on: August 18, 2008, 01:44:07 »

Well, i tried, tried, tried and nothing only still give me null reference  :-\ so my question is i need to have a mediator to UserManagerPanel to instance is components?

I am really stuck on it and i dont understand why it dont instantied it and give me null reference i implemented you solution but the child of UserManager like UserList and UserForm still give me null  :-\ i debug from a lot of different ways and still see the null  :-\

And the stupid thing is, it puts the components in the view but dont give them an instance  :-\

Daniel Gomes
« Last Edit: August 18, 2008, 01:48:32 by danielcsgomes » Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #7 on: August 18, 2008, 04:55:14 »

If the components are in the view, and visible, you have instances. You just aren't specifying them correctly.

One question: are you trying to reach them with getChildByName() by any chance?

Remember above I mentioned Flash uses the name property for this? If you wanted to do appView.getChildByName('loginPanel') for instance, you'd need to specify name="loginPanel" as a property of your LoginPanel in its MXML declaration.

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

 - daniel@onedesign.com.pt
View Profile Email
« Reply #8 on: August 18, 2008, 05:01:09 »

Hi Cliff,

no i am doing it just the way you post in the newer FAQ entry, i will post to you the ApplicationMediator.

:
package com.pt.onedesign.user_manager.view
{

import com.pt.onedesign.user_manager.model.LoginProxy;
import com.pt.onedesign.user_manager.model.UserProxy;

import mx.controls.Alert;
import mx.core.Container;

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
{
//Define Proxy
private var _loginProxy : LoginProxy;
private var _userProxy : UserProxy;

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

// Define const names for change ViewStack views.
public static const LOGIN_PANEL : int = 0;
public static const USER_MANAGER_PANEL : int = 1;

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

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

facade.registerMediator(new LoginPanelMediator(app.loginPanel));
//facade.registerMediator(new  UserListMediator(app.userManagerPanel.userList));
}



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

override public function handleNotification( note:INotification ):void
{
switch ( note.getName() )
{
   case LoginProxy.LOGIN_FAILED:
Alert.show("Invalid username or password, please try it again.","Login Failed");
app.currentViewSelector = LOGIN_PANEL;
break;

   case LoginProxy.LOGIN_SUCCESS:
    app.currentViewSelector = USER_MANAGER_PANEL;
    facade.registerMediator(new UserListMediator(app.activeView.userList));
    facade.sendNotification(UserProxy.GET_USERS_LIST);
        break;
}
}

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

i have and id for userManagerPanel,userList and userForm but i didn't give them a "name" property. I am getting the view this form:

Main.mxml
:
[Bindable] public var currentViewSelector:int = ApplicationMediator.LOGIN_PANEL;
            public var activeView:Object;


]]>
</mx:Script>

  <mx:Binding source="appView.selectedChild" destination="activeView" />
<mx:ViewStack id="appView" resizeToContent="true" selectedIndex="{currentViewSelector}" >
<loginComp:LoginPanel id="loginPanel"/>
<userManagerComp:UserManager id="userManagerPanel"/>
</mx:ViewStack>

The unique difference between my code and yours of the FAQ post is that i dont have a button and i change the view and add the mediator when a LOGIN_SUCCESS notification is send, and my public var currentViewSelector:int and yours is String (but if i change to string give me an error).

Daniel Gomes
Logged
danielcsgomes
Full Member
***
Posts: 47

 - daniel@onedesign.com.pt
View Profile Email
« Reply #9 on: August 18, 2008, 05:35:01 »

I tried now a different approach debugging the app to see what is happens and i see this:

- It changes the view and register the mediator just like the ApplicationMediator i posted.
- Now i add a creationComplete in Main.mxml on the view "userManagerPanel"
- And when it's debbug first change view and register mediator, but it does't create complete the View so because of this i got the null reference

So what is the best way to register the mediator? it's a best practice put a creationComplete in the view to send a notification to register the mediator?
Because the problem i am getting is that it register the Mediador but the View isn't totally build so when i register the mediator the view are loading and don't instanciate the userList.

I have to thank you cliff by all the support and assistance you have given, thank you.

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



View Profile WWW Email
« Reply #10 on: August 18, 2008, 11:54:40 »

On thing you are doing differently is that you are doing: facade.registerMediator(new LoginPanelMediator(app.loginPanel)); in the constructor of the ApplicationMediator.

In the FAQ, I'm showing facade interaction being deferred until onRegister, with nothing but the call to super in the constructor. Though this isn't the problem, it should be pointed out.

The best practice that has emerged (but is not yet reflected in many demos) is to hold off facade interaction until onRegister. This allows the Mediator or Proxy to be sure it's ready to participate in any conversations it may start. If it isn't registered and it performs an action that results in a Notification it should receive, it will miss that Notification. If another actor needs to retrieve it as a result of this action, that actor will not succeed unless the initiating actor is already registered.

Another good reason to adopt this practice is that in MultiCore, it is required. You cannot access the concrete facade from the constructor of your Notifier sublcasses in MultiCore. There, you must wait until initializeNotifier (unique to MultiCore, and prior to registration), and onRegister, which is more desirable for the same reasons stated above for the Standard version.

What your actual problem is a Flex timing thing. You are attempting to use the reference to the new activeView object immediately after setting selectedIndex.


:
   case LoginProxy.LOGIN_SUCCESS:
    app.currentViewSelector = USER_MANAGER_PANEL;
    facade.registerMediator(new UserListMediator(app.activeView.userList));
    facade.sendNotification(UserProxy.GET_USERS_LIST);
                break;

It stands to reason that the activeView object should be available because setting app.currentViewSelector via the magic of Flex Binding causes:

  * The ViewStack index to change,
  * The new child to be created,
  * The activeView property to be set to that new child

However Flex Binding and display updates don't always happen immediately. Sometimes it doesn't happen until the next frame. Can't say much with this other than that's how it is. Dealing with it can seem like voodoo. If I can't expect it to be available immediately, then how long? Should I set a timer and then work with that child later?

Usually it is available the next frame, ENTER_FRAME is being trapped for the view updates within the Flex framework. If you debug an application you'll usually find UIComponent.callLaterDispatcher and UIComponent.callLaterDispatcher2 it on your call stack. The latter removes ENTER_FRAME and RENDER event listeners.

If you notice in the FAQ I'm actually calling another method checkForMediator, which figures out which Mediator to register. This pushes a whole new context node onto the running program's stack, it seems to be enough work to get you to the next frame.

This is still not as good as following a protocol so, I'm refactoring the code in the FAQ and creating a demo today that eliminates the guesswork. More soon.

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

 - daniel@onedesign.com.pt
View Profile Email
« Reply #11 on: August 18, 2008, 01:45:44 »

Hi Cliff,

After your post i changed my code and follow exactly the FAQ structure and the timming problem continues, i attach the source code.

This is still not as good as following a protocol so, I'm refactoring the code in the FAQ and creating a demo today that eliminates the guesswork. More soon.

Ok, i will wait for some news ;) and thanks for your help, but was been a really nightmare trying make it work ;)

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



View Profile WWW Email
« Reply #12 on: August 18, 2008, 07:06:20 »

The new demo is called Slacker. Read about it and check it out here:
http://puremvc.org/content/view/92/1/

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

 - daniel@onedesign.com.pt
View Profile Email
« Reply #13 on: August 19, 2008, 12:48:03 »

The key for solution was that creationComplete ;) Thanks for Demo, today maybe i can finish my UserManager Demo.

Thanks Cliff,

Daniel Gomes
Logged
Pages: [1]
Print