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 4
Print
Author Topic: Loadup - A PureMVC AS3 Utility  (Read 62610 times)
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« on: August 16, 2009, 07:25:13 »

The Loadup utility offers a solution to the problem of how to manage the asynchronous loading of Model resources at anytime during application runtime. It supports loading order dependencies, and is progress-aware.

The utility has historically been located here: http://trac.puremvc.org/Utility_AS3_Loadup
The project has been moved here: https://github.com/PureMVC/puremvc-as3-util-loadup/wiki

The author is Philip Sexton.

NOTE: This project was formerly known as StartupManager, and was renamed Loadup, to dispel the notion that the utility it was only usable at startup time.

The previous thread is here: http://forums.puremvc.org/index.php?topic=259.0
« Last Edit: September 23, 2012, 01:28:19 by puremvc » Logged
philipSe
Sr. Member
****
Posts: 139


View Profile Email
« Reply #1 on: August 18, 2009, 03:15:53 »

As regards features, the main enhancement in this release is the option to supply a proxy name to LoadupMonitorProxy, thus enabling independent instances to exist.  See the notes in version.txt.

To migrate to Loadup from StartupManager, see the instructions in the text file migrationToV2.txt.  In developing Loadup, the opportunity was taken to drop code that existed only for backward compatibility purposes.

The two demos have been migrated to Loadup, so we have Loadup as Ordered and Loadup for Assets.  The latter has been enhanced to illustrate the loading of intermittent single assets and a group of assets, as independent tasks.

----Philip
Logged
bestbuyernc
Full Member
***
Posts: 21


View Profile Email
« Reply #2 on: August 19, 2009, 06:53:25 »

Hello.  This is my first major flex/puremvc project.  I am still on the login/logout functionality but its helped me get started with what the players are supposed to do.  It has taken me long to get going but I think I am making good progress thanks to everyone here.

However, I am still confused about a few things like, THE STATE MACHINE, when I should use a command vs just calling the proxy method from the mediator, how to keep viewComponent code out of the mediator by sending notifications that invoke commands and what it means to 'put business logic in the commands'  when the only thing I typically see in commands in examples is the command getting data from the note and invoking a proxy method. I hoping to find a good complex command example.

Anyway, for this post my question is about a sequencing issue.
My login view has 4 states.  Login, Registration, Reset Password and Change Password.  When the user registers successfully I show an alert telling them that the registration was successful.  When the user clicks the alert 'OK' button, I want the following to happen:

1.  Enable the form.
2.  Clear the form.
3.  Go back to the Login state.
4.  Populate the userID with the userID from the registration VO.

To go back to the login state I am using an approach I found where the application proxy holds the view state property and sends a notification any time its changed and then the app mediator reacts by changing the view.  But the login form mediator is responsible for enabling the login form and clearing the form.  What is happening is that the form gets cleared and enabled but the app mediator doesn't get a chance to change the state.  Its a sequence issue because the app mediator gets the notification first and changes the state but I think calling the alert from the registration state makes it return.  Not sure.
I am wondering if this is where the State Machine or Macro Commands would come in handy.  The State Machine has been really intimidating to me but I knew at some point I would have to face it.  From what I understand Macro Commands are a way of chaining and sequencing events. My startup command is a macro command.  Is this what I should be doing in this scenario?  Or is this something the state machine would be good for.

Thanks.

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



View Profile WWW Email
« Reply #3 on: August 20, 2009, 07:23:40 »

Don't keep state in a proxy. State is a View concept, not part of the Domain Model and thus not a responsibility belonging in the Model tier. Instead use the State Machine. Check out the overview presentation at http://puremvc.tv to get an idea of how it works.

As for a complex example that also uses State Machine, you can see the original Sea of Arrows code at http://seaofarrows.com/srcview. Check out the state machine in the shell. It executes a number of commands that do chunky business logic like plumbing the modules.

The long and short of when to fire a command instead of calling a proxy method from a mediator is, you can do either. If there's a lot of preparation involved, or if the same call will be triggered from multiple places in the UI, you might be better making the call from a command, so you'll only have one place to refactor if the method signature on the proxy changes. Otherwise, the View and Controller tiers are both allowed to update the Model.

-=Cliff>

Logged
bestbuyernc
Full Member
***
Posts: 21


View Profile Email
« Reply #4 on: August 21, 2009, 11:16:07 »

Thank you for responding.  What about the states of one component vs. application state?  My login screen component has 4 different states. LOGIN, REGISTER, CHANGE PASSWORD and RESET PASSWORD.  Should all of these be managed by the state machine? Or is that for more application scope state, like moving from the Login page to the Main application page?  Should the view component state be handled only by the component's mediator? Since the first application state change would be once the user logged in successfully. Until that point everything is on one view. 


Also, I want to load a list of projects to a combo box prior to showing the login view.  I am attempting to do this by mapping an APPLICATION.STARTUP action to an APPLICATION.LOAD_PROJECTS.  I want my application mediator to respond to the notification and call the projectProxy to load the projects.  I have defined the FSM like this:

:
<fsm initial={ApplicationFacade.STARTING}>
<state name={ApplicationFacade.STARTING}><transition action={ApplicationFacade.ACTION_STARTUP} target={ApplicationFacade.LOAD_PROJECTS}/>
<transition action={ApplicationFacade.STARTUP_FAILED} target={ApplicationFacade.STARTUP_FAILED}/>
</state>
<state name={ApplicationFacade.LOAD_PROJECTS}><transition action={ApplicationFacade.LOAD_PROJECTS} target={ApplicationFacade.PROJECTS_LOADED}/>
<transition action={ApplicationFacade.PROJECTS_LOADING_FAILED} target={ApplicationFacade.PROJECTS_LOADING_FAILING}/>
</state>
</fsm>;


Thank you.
« Last Edit: August 21, 2009, 12:17:16 by bestbuyernc » Logged
polykrom
Jr. Member
**
Posts: 18

 - polykrom@flashcodeurs.com
View Profile WWW Email
« Reply #5 on: August 21, 2009, 11:30:54 »

Hi,

If you are in a StateMachine logic for the main application, you could use StateMachine in all other "components" or Modules... Each Core can have its own StateMachine (you have to use the multicore package for that).

The way you use StateMachine seems not to be the right way.. You should have one State Machine for your main app with two views LOGIN and POPULATE COMBO.
And in each view, other StateMachines or Notifications handlers to swap between components view.
 
For your FSM, i think the one you provide would be for the Project loading module... not for the main one.. and don't forget to define the target state in your FSM :

ApplicationFacade.STARTUP_FAILED,ApplicationFacade.PROJECTS_LOADED and ApplicationFacade.PROJECTS_LOADING_FAILING

regards

:::Poly:::
« Last Edit: August 21, 2009, 11:48:38 by polykrom » Logged
bestbuyernc
Full Member
***
Posts: 21


View Profile Email
« Reply #6 on: August 23, 2009, 04:31:28 »

Hi,

If you are in a StateMachine logic for the main application, you could use StateMachine in all other "components" or Modules... Each Core can have its own StateMachine (you have to use the multicore package for that).

The way you use StateMachine seems not to be the right way.. You should have one State Machine for your main app with two views LOGIN and POPULATE COMBO.
And in each view, other StateMachines or Notifications handlers to swap between components view.
 
For your FSM, i think the one you provide would be for the Project loading module... not for the main one.. and don't forget to define the target state in your FSM :

ApplicationFacade.STARTUP_FAILED,ApplicationFacade.PROJECTS_LOADED and ApplicationFacade.PROJECTS_LOADING_FAILING

regards

:::Poly:::
Hi.  I am still confused.  I just need to projects to load before the user can interact with the app.  Maybe the startup monitor is more appropriate since it is a startup resource?  I was trying to define my FSM so that after puremvc is started up the projects would load to the combo box.  Still not getting it.
Logged
polykrom
Jr. Member
**
Posts: 18

 - polykrom@flashcodeurs.com
View Profile WWW Email
« Reply #7 on: August 23, 2009, 11:33:02 »

Hi,

so, if it's just to be sure your comboBox content is loaded, you can use StartupManager and, when the content is loaded, send a stateMachine.ACTION notification to swap to the comboBox view and use a bindable var wich contain the combo values to populate the component...
« Last Edit: August 24, 2009, 01:13:34 by polykrom » Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #8 on: August 24, 2009, 10:18:57 »

View components can have their own states. If you're using Flex, states are built in. But if not, don't try to burden the Mediator with statekeeping for its view component.


-=Cliff>
Logged
bestbuyernc
Full Member
***
Posts: 21


View Profile Email
« Reply #9 on: August 25, 2009, 08:10:39 »

Thank you Cliff.  That's what I was doing. 

PolyKron. You are right. I was trying to get the State Machine to do what the Loadup was meant to do.  I got it to work. Somewhat. I created my ProjectProxy as an ILoadupProxy. 

I thought I could send the proxy's project data with the ApplicationFacade.PROJECTS_LOADED notification.  I was following the LoadupAssets example. It sends the notification as sendNotification( ApplicationFacade.PROJECTS_LOADED, NAME, SRNAME ); I am not sure of the purpose of SRNAME.  So to get my list of projects I had to send another notification sendNotification( ProjectProxy.PROJECTS_RETRIEVED, data  );.  But doing it this way I think may be overriding the purpose of the Loadup utility in terms of blocking until the startup data is available.  When I run it the UI is displayed and then a few seconds later the combo box is populated.  Is this where the state machine would come in handy?
At first I had my LoginFormMediator listening for the LoadupMonitorProxy.LOADING_COMPLETE: notification.  Now my ApplicationMediator is interested LOADING_COMPLETE but I am not doing anything with the note.  I don't want the login page displayed if the project fail to load so maybe the ApplicationMediator should be responsible for handling that scenario?
Is the following the correct way to return the Proxy's project data to the interested mediator?  Am I breaking the chain of events by sending both notifications when the data is retrieved from the Project Proxy?

Thank you.

:

public class ProjectProxy extends EntityProxy implements ILoadupProxy
{
        //  send LoginAttempt as proxy data
public function ProjectProxy() {
super( ProjectProxy.NAME );
}

// notification name constants
public static const NAME:String = "ProjectProxy";
public static const SRNAME:String = 'ProjectSRProxy';
public static const PROJECTS_RETRIEVED:String = NAME+'/notes/projects_retrieved';
public static const PROJECTS_RETRIEVAL_FAILED:String = NAME+'/notes/projects_retrieval_failed';

// project service
        private var projectService:RemoteObject;
        private var tryCount:int = 0;
       
// configure the proxy when registered
public override function onRegister():void {
// configure the remote object
        projectService  = new RemoteObject("GenericDestination");
        projectService.source = "ProjectService";

        // is this ok? Had do listen for the remote call.  I thought this would be inherited behaviour
        projectService.getOperation("getProjects").addEventListener( ResultEvent.RESULT, loaded );
        projectService.getOperation("getProjects").addEventListener( FaultEvent.FAULT, failed );
}

// call the remote object service
public function load():void {
        tryCount++;   
        sendNotification( ApplicationFacade.PROJECTS_LOADING, NAME, SRNAME );
        projectService.getProjects();
}
       
// remote procedure is successfull
public function loaded( asToken:Object=null ):void {       
        var projects:Array = [];
        for each (var item:Object in asToken.result ) {
        projects.push( { label: item.domain, data: item.domain } );
        }
         
setData( projects );
        // not sure what this is for
        sendNotification( ApplicationFacade.PROJECTS_LOADED, NAME, SRNAME );
        // this is how I am sending the retrieved data.
      sendNotification( ProjectProxy.PROJECTS_RETRIEVED, data  ); 
}
       
// remote login procedure is successfull
public function failed( asToken:Object=null ):void {         
    sendNotification( PROJECTS_RETRIEVAL_FAILED ); 
}
« Last Edit: August 25, 2009, 09:47:43 by bestbuyernc » Logged
philipSe
Sr. Member
****
Posts: 139


View Profile Email
« Reply #10 on: August 26, 2009, 05:10:46 »

Just to clarify about 'loaded' notifications in the Loadup utility.  The release notes in version.txt state:
    On loaded and failed notifications sent by the client app, if
    the monitor proxyName is not the default name, the notification
    type MUST BE the monitor proxyName.

So, type should be empty unless the LoadupMonitorProxy has been given a custom name.

This business of SRNAME may have arisen from the LoadupAsOrdered demo, where there are code instances like sendLoadedNotification( blah_LOADED, NAME, SRNAME ). This 'sendLoadedNotification' method is local to the demo; it is not part of the Loadup API.
----Philip


Logged
bestbuyernc
Full Member
***
Posts: 21


View Profile Email
« Reply #11 on: August 27, 2009, 07:00:34 »

Just to clarify about 'loaded' notifications in the Loadup utility.  The release notes in version.txt state:
    On loaded and failed notifications sent by the client app, if
    the monitor proxyName is not the default name, the notification
    type MUST BE the monitor proxyName.

So, type should be empty unless the LoadupMonitorProxy has been given a custom name.

This business of SRNAME may have arisen from the LoadupAsOrdered demo, where there are code instances like sendLoadedNotification( blah_LOADED, NAME, SRNAME ). This 'sendLoadedNotification' method is local to the demo; it is not part of the Loadup API.
----Philip

Thanks Philip.
I am getting this error even though I have not give the LoadupMonitorProxy a custom name.

:
Sent ApplicationFacade/action/startup
Sent ApplicationFacade/note/started
Sent ApplicationFacade/action/showSplashScreen
Sent ApplicationFacade/action/loadResources
Sent ApplicationFacade/note/loading_projects
Error: null: Unknown ILoadupProxy in loaded/failed notification, not known in LoadupMonitorProxy instance named :LoadupMonitorProxy
at org.puremvc.as3.multicore.utilities.loadup.model::LoadupMonitorProxy/resolveAppProxyNameElseError()[C:\My Documents\Flex Builder 3\PureMVC\Utility_AS3_Loadup_2_0\multicore\org\puremvc\as3\multicore\utilities\loadup\model\LoadupMonitorProxy.as:485]
at org.puremvc.as3.multicore.utilities.loadup.model::LoadupMonitorProxy/resourceLoaded()[C:\My Documents\Flex Builder 3\PureMVC\Utility_AS3_Loadup_2_0\multicore\org\puremvc\as3\multicore\utilities\loadup\model\LoadupMonitorProxy.as:448]
at org.puremvc.as3.multicore.utilities.loadup.controller::LoadupResourceLoadedCommand/execute()[C:\My Documents\Flex Builder 3\PureMVC\Utility_AS3_Loadup_2_0\multicore\org\puremvc\as3\multicore\utilities\loadup\controller\LoadupResourceLoadedCommand.as:46]
at org.puremvc.as3.multicore.core::Controller/executSent ApplicationFacade/note/projects_loaded

When the projects are returned from the service I am sending an ApplicationFacade.PROJECTS_LOADED notification that was registered as a resource loaded notification.  I think this wrong because isn't this the notification that loadup sends when the resources have finished loading?  I tried not sending any notifications from the loaded function to see if ApplicationFacade.PROJECTS_LOADED is sent by the Loadup Utiltity, but it is not.

I know these posts have hit a lot of different areas.  But I appreciate your patience and assistance.
Thank you.

:
   
ApplicationFacade
registerResourceLoadedCommand( PROJECTS_LOADED );
registerResourceFailedCommand( PROJECTS_LOADING_FAILED );

ProjectProxy
    // remote login procedure is successfull
        public function loaded( asToken:Object=null ):void {       
        var projects:Array = [];
        for each ( var item:Object in asToken.result ) {
        projects.push( { label: item.domain, data: item.domain } );
        }
setData( projects );
sendNotification( ApplicationFacade.PROJECTS_LOADED, data );
        }

But in the LoadResourcesCommand i am using the default name.

:
public class LoadResourcesCommand extends SimpleCommand implements ICommand
{
private var monitor:LoadupMonitorProxy;

public override function execute( note:INotification ):void {
facade.registerProxy( new LoadupMonitorProxy() );
this.monitor = facade.retrieveProxy( LoadupMonitorProxy.NAME ) as LoadupMonitorProxy;

var projectProxy:ILoadupProxy = new ProjectProxy();
facade.registerProxy( projectProxy );

var rProjectPx:LoadupResourceProxy = makeAndRegisterLoadupResource( ProjectProxy.NAME, projectProxy );
this.monitor.loadResources();
}

private function makeAndRegisterLoadupResource( proxyName :String, appResourceProxy:ILoadupProxy ):LoadupResourceProxy {
var r:LoadupResourceProxy = new LoadupResourceProxy( proxyName, appResourceProxy );
facade.registerProxy( r );
monitor.addResource( r );
return r;
}
}

Logged
bestbuyernc
Full Member
***
Posts: 21


View Profile Email
« Reply #12 on: August 27, 2009, 07:45:13 »

I see my answer.  Right in the documentation.  Thanks.
Logged
philipSe
Sr. Member
****
Posts: 139


View Profile Email
« Reply #13 on: August 27, 2009, 07:59:41 »

'loaded' notifications are sent by your app, they are not sent by Loadup, they are consumed by Loadup.  You must include the proxy name of the ILoadupProxy as the body of the loaded notification - note ILoadupProxy refers to the proxy in your app that has the load() method for the resource; it does not refer to the monitor.  If you don't supply a recognisable name in the body, you get the 'Unknown ILoadupProxy' Error.

When Loadup judges that loading of all resources is complete, on account of having received a loaded notification for each resource, then Loadup sends a LOADING_COMPLETE notification.
----Philip
Logged
bestbuyernc
Full Member
***
Posts: 21


View Profile Email
« Reply #14 on: August 27, 2009, 11:29:42 »

Yes.  I got rid of that error. By sending my ILoadupProxy (ProjectProxy.NAME) in the resource loaded command notification. 

But... this brings me right back to an earlier question.  I have to send another notification from the ProjectProxy to get its data. So my ApplicationFacade reacts to the ApplictionFacade.PROJECTS_LOADED note and changes to the Login view (just until I figure out the STATE MACHINE) and my LoginMediator responds to the ProjectProxy.PROJECTS_RETRIEVED note and tries to populate the combo box with the data from the ProjectProxy.  Everything seems to be working.   I'm just wondering if it is ok to do it this way.

Thanks.

:
ProjectProxy
        // remote login procedure is successfull
        public function loaded( asToken:Object=null ):void {       
        var projects:Array = [];
        for each ( var item:Object in asToken.result ) {
        projects.push( { label: item.domain, data: item.domain } );
        }
setData( projects );
sendNotification( ApplicationFacade.PROJECTS_LOADED, ProjectProxy.NAME );
sendNotification( ProjectProxy.PROJECTS_RETRIEVED, data );
        }

ApplicationFacade
case ApplicationFacade.PROJECTS_LOADED:
       app.appView.selectedIndex = 2;
break;

LoginMediator
case ProjectProxy.PROJECTS_RETRIEVED:
       loginForm.projectSource = ( body as Array );
break;
 
Logged
Pages: [1] 2 3 4
Print