Handling Popup Logic in PureMVC *Update*

I have since re-structured some of my logic in handling popups in a Flex application using PureMVC. If you refer to my previous post you will see that I have a PopManager that extends PopUpManager and the core logic to handle opening and closing Popups was via the Mediator. I have now ripped this logic out so it is handled by a Command and popups can now be opened/closed via a notification, cleaner, simpler and more logical to PureMVC.

Here is a summary of how to handle popups more efficiently enabling multiple popup window types i.e. image popups, form popups etc. *note* Multicore SWC used:

PopManager

Actionscript:
  1. public class PopManager extends PopUpManager
  2. {
  3.  
  4. public static function openPopUpWindow( facade:Facade,
  5.                                                                         ComponentClass:Class,
  6.                                                                         MediatorClass:Class,
  7.                                                                         modal:Boolean,
  8.                                                                         x:Number, y:Number,
  9.                                                                         name:String,
  10.                                                                         centre:Boolean ):void
  11. {
  12.     var window:IFlexDisplayObject = PopUpManager.createPopUp( Application.application as Sprite,
  13.                                                                                                         ComponentClass, modal );
  14.        
  15.     facade.registerMediator( new MediatorClass( window ) );
  16.            
  17.     if( centre != true )
  18.     {
  19.         window.x = x;
  20.         window.y = y;
  21.     }
  22.     else
  23.     {
  24.         PopUpManager.centerPopUp( window );
  25.     }      
  26.     window.name = name;
  27.            
  28. }
  29.        
  30. public static function closePopUpWindow( facade:Facade,
  31.                                                                  window:IFlexDisplayObject,
  32.                                                                  mediatorName:String ):void
  33. {
  34.     PopUpManager.removePopUp( window );
  35.     facade.removeMediator( mediatorName );
  36. }
  37.  
  38. }

HandlePopCommand

Actionscript:
  1. public class HandlePopCommand extends SimpleCommand implements ICommand
  2. {
  3.     override public function execute( note:INotification ):void
  4.     {
  5.         var pop :P opInstanceVO  = new PopInstanceVO();
  6.         var pop_registered:Boolean;
  7.         var openData:PopDataVO = note.getBody() as PopDataVO;
  8.  
  9.         switch ( note.getType() as String )
  10.         {
  11.             case ResourceConstants.OPEN_POP:
  12.                 switch ( openData.type )
  13.                 {
  14.                     case ResourceConstants.CUSTOM_POP:
  15.                         pop_registered = facade.hasMediator( CustomPopMediator.NAME );   
  16.                         if( !pop_registered )
  17.                         {
  18.                             PopManager.openPopUpWindow( ApplicationFacade.getInstance( this.multitonKey ),
  19.                                             CustomPop,
  20.                                             CustomPopMediator,
  21.                                             true,
  22.                                                 0, 0,
  23.                                             "custom_pop",
  24.                                             true );
  25.                         }
  26.                         setPopData( CustomPopMediator.NAME, openData );
  27.                         break;
  28.                 }
  29.                 break;
  30.                    
  31.             case ResourceConstants.CLOSE_POP:
  32.                 switch ( note.getBody() as String )
  33.                 {
  34.                     case ResourceConstants.CUSTOM_POP:
  35.                         pop_registered = facade.hasMediator( CustomPopMediator.NAME );
  36.                         if( pop_registered )
  37.                         {
  38.                             var customPopMed:CustomPopMediator = facade.retrieveMediator( CustomPopMediator.NAME ) as CustomPopMediator;
  39.                            
  40.                             pop.window            = customPopMed.getViewComponent() as IFlexDisplayObject;
  41.                             pop.mediator          = CustomPopMediator.NAME;
  42.                                
  43.                             PopManager.closePopUpWindow( ApplicationFacade.getInstance( this.multitonKey ), pop.window, pop.mediator );
  44.                         }
  45.                         break;
  46.                 }
  47.                 break;
  48.         }
  49.     }
  50.        
  51.     private function setPopData( name:String, data :P opDataVO ):void
  52.     {
  53.         switch ( name )
  54.         {
  55.             case "CustomPopMediator":
  56.                 if( facade.hasMediator( CustomPopMediator.NAME ) )
  57.                 {
  58.                     var med:CustomPopMediator =
  59.                                                         facade.retrieveMediator( CustomPopMediator.NAME ) as CustomPopMediator;
  60.                     med.popData = data;
  61.                 }
  62.                 break;
  63.         }
  64.     }
  65. }

CustomPopMediator

Actionscript:
  1. public class CustomPopMediator extends Mediator implements IMediator
  2. {
  3.     public static const NAME:String = 'CustomPopMediator';
  4.  
  5.     private var __popData:PopDataVO;
  6.  
  7.     public function CustomPopMediator( viewComponent:Object )
  8.     {
  9.         super( NAME, viewComponent );
  10.     }
  11.  
  12.         public function set popData( val:PopDataVO ):void
  13.         {
  14.             __popData = val;
  15.     }
  16.  
  17.     override public function onRegister():void
  18.     {
  19.         pop.addEventListener( CustomPopMediator.CLOSE_POP, onClose );
  20.     }
  21.        
  22.     protected function onClose( e:Event ):void
  23.     {
  24.         sendNotification( ApplicationFacade.HANDLE_POP, ResourceConstants.CUSTOM_POP, ResourceConstants.CLOSE_POP );
  25.     }
  26.        
  27.     protected function get pop():CustomPop
  28.     {
  29.         return viewComponent as CustomPop;
  30.     }
  31.    
  32. }

PopDataVO

Actionscript:
  1. public class PopDataVO
  2. {
  3.     public var type:String;
  4.     public var title:String;
  5.     public var body:Object;
  6.        
  7.     public function PopDataVO(){}
  8. }

PopInstanceVO

Actionscript:
  1. public class PopInstanceVO
  2. {
  3.     public var window:IFlexDisplayObject;
  4.     public var mediator:String;
  5.     public var name:String;
  6.     public var x:Number;
  7.     public var y:Number;
  8.        
  9.     public function PopInstanceVO(){}
  10. }

CustomPop is simply an mxml component extending TitleWindow.

You can now call out CustomPop like so:

Actionscript:
  1. var pObj:PopDataVO = new PopDataVO();
  2. pObj.type                 = ResourceConstants.CUSTOM_POP;
  3. pObj.body                = "newtriks.com";
  4.  
  5. sendNotification( ApplicationFacade.HANDLE_POP, pObj, ResourceConstants.OPEN_POP );

And then close it like so:

Actionscript:
  1. sendNotification( ApplicationFacade.HANDLE_POP, ResourceConstants.CUSTOM_POP, ResourceConstants.CLOSE_POP );

This entry was posted in AS3, Flex, PureMVC and tagged , , , , , . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

23 Comments

  1. cease
    Posted March 11, 2009 at 12:56 pm | Permalink

    Thanks for this , a lot of good stuff here. Two questions maybe you can help me out with. ( sort of a newb here)
    1. On my mxml of CustomPop I have a bindable STring called netSt. I try to set this in the mediator in the onRegister function like this
    pop.netSt = __popData.body as String
    Now the problem is the onRegister of the mediator gets called and executes before the setPopData defined in the HandlePopCommand.
    Can you let me know if you are able to verfiy this and if so, is there a solution or something i'm doing wrong. thanks

  2. Posted March 11, 2009 at 1:07 pm | Permalink

    Cease looking at the above, your trying to retrieve .body() which is normally on your notification in which case should be managed within the handleNotification() method!

    If your trying to pass data to the mediator and in turn to the popup then do as I have in the HandlePopCommand using the setPopData() method. This first checks to see if the mediator is registered then using a public setter in the popups mediator assigns the data. This data can be then assigned to the popup or if its Bindable will update the popups data!

    HTH,

    Simon

  3. cease
    Posted March 12, 2009 at 10:32 am | Permalink

    Hi
    Thanks for the reply, yeah the scenario described is exactly what I was trying to accomplish, just displaying the data on the view. What I did was to assign the public variable found in the view mxml file in the setter like this
    public function set popData( val:PopDataVo ):void
    {
    _popData = val;
    viewPage.voobj = _popData.body as VoObj;
    }
    This seems to work. Is this what you would have suggested ?

  4. Posted March 12, 2009 at 10:41 am | Permalink

    No I don't suggest that Cease! Do not assign properties directly on the View from anywhere else BUT your Mediator. So set the pop data in a public setter in your Mediator from the Command. The Mediator has a one to one relationship with your View and therefore assigns all the data to the View NOT the Command :)

  5. Lee
    Posted April 28, 2009 at 3:17 pm | Permalink

    Thanks for this post Simon, I've been wondering how to manage popups in my PureMVC app and this works a treat. How could you use this in a Modular app where, for example, all error messages from the Modules were handled by the Shell in a popup? Would you need the Pipes utility to do this?

  6. Posted April 28, 2009 at 3:26 pm | Permalink

    Hey Lee glad you found use mate! Regarding your question, Pipes would probably be an easy was to manage this, sling the notes down a pipe to the Shell. Not yet done this but would be looking to some time soon. Would be keen to hear how you get on :)

  7. Lee
    Posted April 28, 2009 at 3:46 pm | Permalink

    Thanks for the quick reply Simon. I'm not using Pipes in this app but I think now might be the time to start! Hopefully shouldn't require too much refactoring :)

  8. Bruce Flowers
    Posted June 15, 2009 at 8:27 pm | Permalink

    Why not just listen for the FlexEvent.ADD event from component you're adding via PopupManager? Much less code, and it still allows you to add a mediator in an "onAdded" handler, using Facade.getInstance().registerMediator...

  9. Posted June 16, 2009 at 9:23 am | Permalink

    Hey Bruce,

    Yup agreed but (if I understand you correctly) for a few projects I was handling popups within, I needed to instantiate popups from outside of the View logic i.e. a proxy. This method, although more code meant that I had a central place to generate, populate and remove popups which could be accessed via notifications.

    Cheers,

    Simon

  10. Claudiu
    Posted July 22, 2009 at 8:44 am | Permalink

    How about if I need to use this mechanism for more that one pop-up? Since CustomPop is "hard-coded" in the mediator what's the better way to tackle that?
    Change the mediator to check if an instance of the PopInstanceVO has been injected upon notification ?

    Cheers,
    Claudiu

  11. Posted July 22, 2009 at 10:53 am | Permalink

    Hi Claudiu,

    Well there are a couple of options. If I have multiple popups of differing types I treat them like I do other viewComponents with their own mediator. So for example I have a popup to display load progress, a popup for a login form etc. The handle pop command already has the logic to process different types using a switch statement, in my example there is only one type so far (ResourceConstants.CUSTOM_POP, if you want to add more simple add to the switch statements in this command as you see fit.

    HTH,

    Simon

  12. Claudiu
    Posted July 22, 2009 at 12:43 pm | Permalink

    Actually that is exactly what I did it hit me after posting (it hit me suddenly that i can add one or more custom pop-ups to the command), the only small downside to this is that the mediator cannot cast to a certain component anymore, and each component uses the same mediator. This is fine as long as I won't encounter one pop-up launching another cause the the mediator will already have the first pop-up registered... But so far I don;t see this happening

    10x for the reply,
    Cheers,
    Claudiu

  13. Posted July 22, 2009 at 12:56 pm | Permalink

    Not quite sure if I get your problem Claudiu, each view has its own mediator, there should be no sharing, unless of course you mean that your trying to open multiple instances of the same view component?

  14. Claudiu
    Posted July 22, 2009 at 1:25 pm | Permalink

    Why have let's say 3 mediators for 3 pop-ups since I only one them to be shown by this shown and closed by this mechanism? I will have lots of duplicate code just for changing the pop-up name ... Am I missing something?

    Claudiu

  15. Posted July 22, 2009 at 1:37 pm | Permalink

    Currently the only potential repetition is the code in the switch statement i.e. for checking if the popup mediator is registered and then calling the PopManager.openPopUpWindow() method which is minor. That complete means you now have the popup either registered or unregistered with the facade. The final code snippets at the end of my post show how to open and close a popup and you would only need to change the body() attribute of the notification for each popup you want to open/close. So unless I am completely misunderstanding you there should be minimal duplicate code.

  16. Claudiu
    Posted July 22, 2009 at 1:51 pm | Permalink

    Ok so if I have a message pop-up and a login pop-up I want both of them to be handled by the same mediator instead of writing 2 mediators since i can extend the case from command. I just want to use this for showing and hiding them ... Each op-ups will have the business logic handled outside this ... wel maybe the message will be injected with data when called but aside from that i don't see why register 2 mediators when I can show and hide different pop-ups with just one... If I had 2 mediators, the only place where one will be different from the other will be the getter that casts to the to the actual view component and the component class ref ... Am I making sense here?

    C

  17. Posted July 22, 2009 at 2:19 pm | Permalink

    well have one view component with 2 viewStates and when you inject the data pass a param to define the state to show, easy ;)

  18. yeremy
    Posted August 3, 2009 at 5:25 pm | Permalink

    Thanks! :)

  19. Jason
    Posted September 11, 2009 at 6:53 pm | Permalink

    So when I use these classes and I have my popup dispatching and event the mediator doesn't hear the event. If I attach an eventlistener on the main stage to listen for the events from the popup the stage hears the events.

    Here is how I instantiate the popup from the stage.

    var pObj:PopDataVO = new PopDataVO();
    pObj.type = ChooseViewIntroPanel.CHOOSE_VIEW_INTRO_PANEL;

    facade.sendNotification( ApplicationFacade.HANDLE_POP, pObj, ResourceConstants.OPEN_POP );

    You might say you shouldn't do that from the stage, but rather from a command or another mediator like the stageMediator.

    The issue is this popup is the firs thing that happens when the app loads. Think of a popup you get in dreamweaver or photoshop.

    J

  20. Posted September 11, 2009 at 7:10 pm | Permalink

    So you want to load a popup on application creation complete, do you mean without any user interaction? It depends on your setup a suggestion would be to use your startup command to fire off the HANDLE_POP notification, or onRegister() in your ApplicationMediator, etc..... Is this what you mean?

  21. Jason
    Posted September 17, 2009 at 8:24 pm | Permalink

    Ok, got it thanks.

    So, if I want the popup to appear at the top and direct center (horiz) of a certain component on stage, how would I go about doing that.

    So I click a component on screen, say a sprite, I want my popup to appear 10 pixels above the component I clicked and centered horiz over the clicked component?

    Any suggestions?

  22. Posted April 7, 2010 at 5:17 pm | Permalink

    i had to make a few adjustments to this but.... this worked like a charm and saved me a ton of time!

  23. Dave
    Posted May 27, 2010 at 3:53 pm | Permalink

    Hi Simon,
    Just tried implementing your code but keep getting this error:
    "TypeError: Error #1009: Cannot access a property or method of a null object reference."
    At the line:
    "PopManager.openPopUpWindow( ApplicationFacade.getInstance( this.multitonKey ),
    CustomPop,
    CustomPopMediator,
    true,
    0, 0,
    "custom_pop",
    true );"
    In the PopManager class.
    Obviously I've modified this all to fit in with my existing app.
    It would appear that the something hasn't finished being initiated before it's being called. But, I've tried tracing it all out and can't spot anything wrong.
    Any ideas? Has the PureMVC swc been updated since this demo?
    Thanks!

One Trackback

  1. [...] This post was mentioned on Twitter by Dennis Plucinik. Dennis Plucinik said: Reading: Handling Popup Logic in PureMVC *Update*: http://bit.ly/bdYLos [...]

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>