opening and closing a popup from commands

corey's Avatar

corey

14 Feb, 2012 08:20 AM

I want to show the busy indicator in a skinnablepopup container every time I load data from http service.

Since I need to show the busy indicator often I figured it would be easiest to just dispatch an event and have the command show the busy icon.

This works good, but I also need to close the busy icon from a command as well. The problem is that I cannot inject the popup when I need to close it. All the examples provided thus far only cover closing popups from a view or view mediator.

[Inject] public var view:BusyPopUp;

Error: Injector is missing a rule to handle injection into property "view" of object "[object CloseBusyCommand]". Target dependency: "com.lmc.remoteadmin.components::BusyPopUp", named ""

What am I doing wrong?

public class ShowBusyCommand extends Command
    {
        public function ShowBusyCommand()
        {
            super();
        }
        override public function execute():void{
            // create the SkinnablePopUpContainer
            var mPopUp:BusyPopUp = new BusyPopUp();
            // set the styles
            this.mediatorMap.createMediator(mPopUp);
            mPopUp.positionPopUp();
            mPopUp.open(FlexGlobals.topLevelApplication as DisplayObjectContainer);
        }

    }

public class CloseBusyCommand extends Command
    {
        [Inject] public var view:BusyPopUp;

        public function CloseBusyCommand()
        {
            super();
        }
        override public function execute():void{
            view.close(true);
            this.mediatorMap.removeMediatorByView(model);
        }

    }
  1. Support Staff 1 Posted by Ondina D.F. on 14 Feb, 2012 09:55 AM

    Ondina D.F.'s Avatar
  2. Support Staff 2 Posted by creynders on 14 Feb, 2012 09:56 AM

    creynders's Avatar

    I'd REALLY recommend against doing this in a command.
    It's view logic so it should be handled by the view tier.
    I'd create a separate view/mediator pair that is responsible for showing and hiding the popup.

    And to answer your question, views aren't permanently registered with the system, they're registered temporarily when the view is added to the display list and the corresponding mediator is being created in order to potentially inject the view reference into the mediator. Right after that the view is unregistered again. This is for two reasons:
    1/ if having multiple instances of the same view onstage how would the injector know which view instance it needs to inject into the command?
    2/ it enforces the separation of concerns between the tiers

  3. Support Staff 3 Posted by Ondina D.F. on 14 Feb, 2012 12:55 PM

    Ondina D.F.'s Avatar

    @ creynders The problem with pop-ups is that you have to manually create and unmap their Mediators.
    I agree with you, that opening and closing pop-ups should be View logic, and, in general, that manipulating views in commands is not a good practice.

    I'd create a separate view/mediator pair that is responsible for showing and hiding the popup.

    Let’s say you have an OpenPopUpButton that would trigger the opening of a pop-up.
    You could mediate this OpenPopUpButton and let its mediator create a mediator for the popup.
    So, you could place the OpenPopUpButton in every view that needs such functionality, and, indeed, you wouldn’t need a command in this case.

    But, what if you want to open or close a pop-up due to other user interactions (selectedItem, tool tip, navigating to another view) or some conditions in your code?
    Which view/mediator pair would you use? It should be the view/mediator pair that triggered the opening of the popup, right? But what would you do if you needed the same pop-up in different views? You’d certainly want to avoid having the same code in different mediators.

    Dispatching SomePopUpEvent.OPEN_POPUP or SomePopUpEvent.CLOSE_POPUP, whenever you need to open or close a popup, is all you’d have to do if you’d use commands, and changes to the code would be made in just one place.

    It’s not nice to use commands, but I think it’s an acceptable compromise in the case of popups or similar components (in rl v1). I’d also like to avoid using a command, if possible, but I gave up trying to find another way of managing popups with rl 1.
    It would be really, really great, if you could share with us a better solution!! Something using the SystemManager? A PopUpManager utility, maybe? :)

    And to answer your question, views aren't permanently registered with the system, they're registered temporarily when the view is
    added to the display list and the corresponding mediator is being
    created in order to potentially inject the view reference into the
    mediator. Right after that the view is unregistered again.

    As I understood the question, Corey is talking about popups or callout containers(mobile), which behave like popups.

    How to Mediate a Flex Popup:
    "The problem is that Flex creates the popup "outside" of the context view's display list. Robotlegs has no way to automatically mediate the view component. When using PopUpManger to create popups, it is necessary to manually create the mediator for the view component"

    I’d really like to see a good solution for handling popups in rl v1 :)

    Cheers,
    Ondina

  4. Support Staff 4 Posted by creynders on 14 Feb, 2012 03:26 PM

    creynders's Avatar

    The trick is to pass Application.application.systemManager as the contextView instead of Main.mxml, then popups are automatically mediated.
    See http://knowledge.robotlegs.org/discussions/solutions/4-mediation-of...

    But come to think of it, and looking at the date when I wrote this, this was probably still using Flex 3, and I'm not sure whether it still works in Flex 4.x
    I'd have to look into that.

    But even if it does work in Flex 4.x, you still can't use a command to close the popup due to the limitations I wrote in my previous post.

  5. Support Staff 5 Posted by Ondina D.F. on 14 Feb, 2012 06:19 PM

    Ondina D.F.'s Avatar

    The trick is to pass Application.application.systemManager as the contextView instead of Main.mxml, then popups are automatically mediated. See http://knowledge.robotlegs.org/discussions/solutions/4-mediation-of...

    Yes, I know about it from your post, but I’ve never tried it out.

    But even if it does work in Flex 4.x, you still can't use a command to close the popup due to the limitations I wrote in my previous post.

    Well, there is a way to do that in case of multiple instances of a modal popup opened in the same view, but I admit it’s not the best way. If it’s not a modal popup, there is always just one instance open.
    For multiple instances:
    Every time the opening command is mapping a popup, it generates an id for the popup view and it adds it to an array in a Model.
    The id is used in injector.mapValue() to get an instance of a popup and create its mediator.

    Then in the closing command, the triggering event has a payload with popups’ id, that is then passed to injector.getInstance()’s named:String, and it can close this instance of a popup.
    If you need to close all popups, you can iterate the model’s array and close each instance.
    Something like that :)

  6. 6 Posted by corey on 14 Feb, 2012 06:29 PM

    corey's Avatar

    I actually did try using the systemManager but was getting errors and stuff wasn't working correctly.

    If I do go the route where I close the popup from a mediator will injection happen or do I need to use the getinstance method?

    Also curious what the RL 2.0 version changes regarding popups and this kind of situation.

  7. Support Staff 7 Posted by creynders on 15 Feb, 2012 08:12 AM

    creynders's Avatar

    I actually did try using the systemManager but was getting errors and stuff wasn't working correctly.

    Yeah, it could be it works differently in flex 4.x (I assume you're using flex 4.x?)
    Ondina's work-around using commands should definitely be a possible solution.

  8. Support Staff 8 Posted by Ondina D.F. on 15 Feb, 2012 11:07 AM

    Ondina D.F.'s Avatar

    If I do go the route where I close the popup from a mediator will injection happen or do I need to use the getinstance method?

    I don’t know what you mean, so I’ll use my example from the other thread:
    In SomeView(mediated by SomeMediator ) clicking on a button triggered the opening of SomeCalloutView (Callout container). If you meant injecting or getting an instance of SomeCalloutView in SomeMediator in order to close it and remove SomeCallOutMediator, I would say that would indeed be a bad practice and would really defeat the purpose of a Mediator :) SomeMediator shouldn’t know about or manage any views other than its own (SomeView).
    So, if you want to use mediators for closing a popup and removing its mediator you can do it like so:

    A In case of closing the callout through a button in the callout view:

    1.Simple SomeCalloutView (Callout container without any other mediated views inside) + SomeCallOutMediator

    SomeCalloutView:

    
    protected function onClosePopUp(event:MouseEvent):void
    {
    dispatchEvent(new SomePopUpEvent(SomePopUpEvent.CLOSE_POPUP));
    this.close();
    }
    

    SomeCallOutMediator:

    
    protected function onClosePopup(event:SomePopUpEvent):void
    {
    mediatorMap.removeMediator(this);
    }
    

    2.SomeCalloutView(Callout container) contains CalloutNavigator (ViewNavigator) which contains AnotherView(View)

    SomeCalloutView:

    
    protected function onClosePopUp(event:MouseEvent):void
    {
    dispatchEvent(new SomePopUpEvent(SomePopUpEvent.CLOSE_POPUP));
    this.close();
    }
    

    SomeCallOutMediator:

    
    protected function onClosePopup(event:SomePopUpEvent):void
    {
    mediatorMap.removeMediator(this);
    dispatch(new SomePopUpEvent(SomePopUpEvent.CLOSE_POPUP));
    }
    

    CalloutNavigatorMediator and AnotherMediator listen for it and do: mediatorMap.removeMediator(this);

    B In case of closing the callout from elsewhere (say from SomeView->SomeMediator):

    SomeCallOutMediator, CalloutNavigatorMediator and every other interested mediator would listen for SomePopUpEvent.CLOSE_POPUP (re)dispatched by SomeMediator .

    SomeCallOutMediator:

    
    protected function onClosePopup(event:SomePopUpEvent):void
    {
    mediatorMap.removeMediator(this);
    view.close();
    }
    

    And the other mediators:
    
    protected function onClosePopup(event:SomePopUpEvent):void
    {
    mediatorMap.removeMediator(this);
    }
    

    The difference between the 2 cases:
    A- SomeCalloutView closes itself
    B- SomeCallOutMediator closes SomeCalloutView

    In a previous post, I gave you an example of closing popups in a command because that’s what you’ve asked :)

    In the next days or so, as time permits, I will add different ways of handling the opening and closing of a callout to the code on github, which was actually just a quick change to an older example in response to your question from the other thread. And later, when rl2 will be mature enough, I’ll make an rl2 version as well.

    Did I answer your questions?
    Ondina

  9. Support Staff 9 Posted by Ondina D.F. on 18 Feb, 2012 09:38 AM

    Ondina D.F.'s Avatar

    Just wanted to confirm that using SystemManager, as creynders suggested, works with Flex 4.6.0.

    ContextView.mxml

    Instead of:
    context=new ApplicationContext(this);
    you have to do this:
    context=new ApplicationContext(this.parent);

    where this.parent is [object ContextView_mx_managers_SystemManager]

  10. 10 Posted by corey on 19 Feb, 2012 05:53 PM

    corey's Avatar

    thanks for the reply.

    Here is what I have done.

    1. Created an open popup command that creates the mediator.
    2. In the popup window view mediator I add a listener and then close the window.
    public class BusyPopUpMediator extends Mediator
        {
            [Inject] public var view:BusyPopUp;
            public function BusyPopUpMediator()
            {
                super();
            }
            public override function onRegister():void{
                addContextListener(BusyPopupEvent.CLOSE, onClose);
                
            }
            private function onClose(event:BusyPopupEvent):void{
                view.close();
                this.mediatorMap.removeMediator(this);
            }
        }
    

    This achieves the same result as using a command to close the window but is much clearer and follows best practices.

    Now the one problem I came across is that the popup will sometimes not close because the BusyPopupEvent.CLOSE event is sent before the listener is set up. This only happens with my Test client which generates the data locally instead of using REST calls. Any suggestions?

  11. Support Staff 11 Posted by Ondina D.F. on 20 Feb, 2012 11:01 AM

    Ondina D.F.'s Avatar

    Some general thoughts about the startup process in a rl v.1-flex app, and why the order of code's execution matters: (from a presentation , that's still WIP, due to lack of time)

    A Flex application built with Robotlegs is considered to be ready to consume external services and to respond to user input
    -when the Flex application has completed its startup process
    AND
    -when the framework has completed its wiring process, that is, when the classes extending the MVC framework have been
    • mapped
    • instantiated
    • supplied with their dependencies
    • registered

    If you want, you can take a look at some diagrams illustrating this process:

    Figure 1

    Figure 2

    Figure 3

    Figure 4

    Figure 5

    The creation of Mediators, whose purpose is to mediate the communication between Views and other application tiers, depends on the ADDED_TO_STAGE event, which is dispatched by the Mediators’ Views (Flex components) and listened to by the Robotlegs internal classes: the Context and the MediatorMap.

    When will a Mediator receive data from a Service?
    When it can listen to custom events dispatched by the Service after accessing the external resources.
    When can a Mediator listen to custom events?
    After it has been created.
    When is the Mediator created?
    After its View has been added to the display list, because the Robotlegs internal classes will create a Mediator first after its View has dispatched an ADDED_TO_STAGE event.
    When is a View added to stage?
    After the Application container has been added to the display list.

    But, as you already know by now, Flex creates a popup "outside" of the context view's display list, and you have to create its Mediator manually. That means, popup’s Mediator won’t be ready for communication with other parts of the app before its creation.
    Therefore you’d have to make a service call **after** the popup's Mediator has been created and it has added a listener for the event it is supposed to listen for!

  12. Ondina D.F. closed this discussion on 08 Mar, 2012 09:20 AM.

Comments are currently closed for this discussion. You can start a new one.

Keyboard shortcuts

Generic

? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac