Displaying a view after it has been added to the contextView in a startup command

stephenadams1's Avatar

stephenadams1

28 Mar, 2011 12:44 PM

Hi,
In my RL application I have a number of views, which I want to switch between. In order to register my views I have created a startup command which adds all the views the the contextView using this method:

contextView.addChild(new SingleProductView());
contextView.addChild(new ProductCarouselView());
contextView.addChild(new StandardCarouselView());

How do I tell my views to display? Do I need to dispatch event in a command called when the ContextEvent.STARTUP_COMPLETE event is fired, which the mediators listen for. When they 'hear' this event how does a mediator tell the view to display?

Thanks

Stephen

  1. 1 Posted by matt on 28 Mar, 2011 01:57 PM

    matt's Avatar

    Hi - your views should be displayed already as you have added them to the stage in your startup command. Or do your views initially have nothing visible in them by default and are waiting on the stage to be told when to display themselves?

    If the latter, I would have another view that handles the addition and removal of these views as and when requested by the application. For example a ScreenManager view whose mediator listens for an application event/signal specifying which view should be displayed. Your ScreenManager view then takes care of displaying the requested view and hides any other views as necessary.

    Interested to hear how others manage this part of an application, seems to be a common cause of confusion.

  2. 2 Posted by stephenadams1 on 28 Mar, 2011 02:49 PM

    stephenadams1's Avatar

    Hi,

    Thanks for the reply, yes it does seem to be a common area of confusion. A lot of the examples has a single view or do not move between views like you'll find in an application.

    I like the idea you suggested, but I was wondering if this should be moved to the service layer instead, have a view manager service.

    When the model or a command is updated/called it can call the service, the service then decides what view to make visible and dispatches a change view event. The view mediator hears this event and changes appropriately.

    A couple of thoughts I have on this are, one is there a way to build your views so they can hide themselves automatically when switching to a new view (e.g. the previous view hides itself, without a event being dispatched telling the view to hide itself).

    And do views, when they are added using contextView.addChild(), are they in a 'ready' state when this happens, are they now available to listen for events from the other layers, is that what contextView.addChild() does?

    Stephen

  3. 3 Posted by matt on 28 Mar, 2011 04:31 PM

    matt's Avatar

    i've never felt the need for an additional layer and just let my mediators dispatch the change view event.

    i would keep the showing and hiding of the view separate from the view itself (unless you have a specific requirement for some kind of animated build) - my ScreenManager class simply gets told to show a view by the application - it will then handle the removal of any existing view and the showing of the new view, the view itself does not need to know about the transitions. I do provide interface methods in views that are managed like this that are called at the start and end of the show/hide transitions enabling them to be prepared, activated, deactivated and shut down appropriately.

    from the RobotLegs side of things, only my ScreenManger view is added directly as a child of contextView in the startup command (although the same command may well tell the ScreenManager to show another view). When another view is added to the stage by the ScreenManager, it's mediator is created according to the mappings in your context and it will be ready to respond to any other events from your application, similarly when the ScreenManager removes the view, the mediator is unregistered ( make sure you tidy up any listeners here ) and the view should go into an inactive state and not respond to the application until it is shown again.

    i feel i should add a disclaimer that i'm not using Flex at all - i'm sure there are more flex-centric ways of doing this if you are!

  4. 4 Posted by Abel de Beer on 28 Mar, 2011 08:56 PM

    Abel de Beer's Avatar

    Excuse me if I'm not understanding the problem correctly, but it sounds like you're looking for the Mediator's onRegister() method! Of course each Mediator needs to be mapped to a view component first for this method to be triggered:

    mediatorMap.mapView(MyView, MyViewMediator);

    By default, the MediatorMap adds event listeners to check if an instance of MyView has been added to the stage. This actually happens when you do:

    contextView.addChild(new MyView());

    In response to this event, the MediatorMap calls the Mediator's onRegister() method (among other things). You can use this method to set up your Mediator (e.g. register event listeners) and initialize your view:

    [Inject] public var view:MyView;
    
    override public function onRegister():void
    {
        view.initialize();
    }
    

    Maybe the biggest part of the stuff above was already clear to you, but I thought I'd just write the whole thing down for the sake of clarity.

  5. 5 Posted by stephenadams1 on 29 Mar, 2011 08:07 AM

    stephenadams1's Avatar

    Hi

    Thanks for both answers, both very helpful. The application I'm building is also pure ActionScript, no Flex at all. I'm going to implement this 'screenManager' idea, instead of the service view manager I was planning to build.

    Also thanks (Abel) for the overview of how the onRegister event works. Now I can use the onRegister to initialise my view and the onRemove event to clear the view and clear any events listeners.

    One question though, in the ScreenManagerMediator can other views be added using contextView.addChild()? Well I shall try this to see if it can.

    Stephen

  6. Support Staff 6 Posted by creynders on 29 Mar, 2011 09:13 AM

    creynders's Avatar

    TBH, I don't think this is a good way of managing screens/views.
    First of all the contextView is simply the main container. In a flash project, this means it's the document class. Every time you call addChild it will add the displayObject to the next display list index, ie. on top of the others. It's pretty hard to keep this organized and maintained.
    IMO the adding of display objects to the display list is a responsibility of specific views. So, if we're talking about screens, it would be a container pretty high up in the hierarchy (but certainly not the context view), but a lot of times you'll be wanting to add a display object to the display list of another view and not simply on top of all the rest. In that case it's the responsibility of that specific view to add the display object.

    In general, I've got a MainNavigationModel, which has a list of the various "pages". Then I've got a MainNavigationView + mediator which sets the selectedPage property of the MainNavigationModel (if you're a purist it will do this through a command)
    The PageContainerMediator picks up this change in the model and passes the relevant data to it's PageContainer view, which in turn adds the relevant display object to it's display list.
    This makes it easy to have transitions between pages et cetera that won't be interfering with in-page display list additions/modifications.

    The above basically also applies to all subnavigation, but always independent from the main navigation/pages/views.

    There are other, far more sophisticated/versatile solutions, but for simple sites this is an easy/clear way of working.

  7. 7 Posted by matt on 29 Mar, 2011 09:43 AM

    matt's Avatar

    yup completely agree, perhaps i wasn't explaining very well - my
    ScreenManager is a view, with it's own mediator, and child views are
    added/removed as children of this screen manager view. Only the screen
    manager view is added as a child of the contextView.

    On 29 March 2011 10:13, creynders <
    [email blocked]> wrote:

  8. 8 Posted by stephenadams1 on 29 Mar, 2011 09:51 AM

    stephenadams1's Avatar

    That's how I saw it, one main view manager with children views. These
    children are added and removed using the onRegister/onRemove event
    handlers.

    Stephen

    On 29 March 2011 10:43, matt <
    [email blocked]> wrote:

  9. 9 Posted by matt on 29 Mar, 2011 10:14 AM

    matt's Avatar

    hmm, not really.

    ScreenManagerView is created and added to the stage.
    ScreenManagerMediator listens for change screen events from the application.

    application dispatches a ShowScreenEvent, specifying Screen1View as the
    screen to display, perhaps as a String id or other property of the event.

    ScreenManagerMediator receives this event and tells ScreenManagerView to
    show Screen1View.

    ScreenManagerView displays Screen1View. When Screen1View is added to the
    stage then it's mediator will be created and the mediator's onRegister
    method will subsequently be called, at which point you can intitialise your
    screen however you choose.

    application dispatches a ShowScreenEvent, specifying Screen2View as the
    screen to display.

    ScreenManagerMediator tells ScreenManagerView to show Screen2View.

    ScreenManagerView removes Screen1View from the stage, at which point
    Screen1Mediator's onRemove method will be called, where you can tidy up/shut
    down the screen. At the same time or once Screen1View has been removed
    (depending on your transition), ScreenManagerView displays Screen2View, and
    Screen2Mediator's onRegister method is called.

    and so on!

    the idea is that the screens themselves know nothing about this whole
    process, they just get told (if necessary) when to go to work and go back to
    sleep by their mediators onRegister and onRemove methods. One thing to
    remember is that a view's mediator does not exist until the view is added to
    the stage.

    On 29 March 2011 10:51, stephenadams1 <
    [email blocked]> wrote:

  10. 10 Posted by stephenadams1 on 29 Mar, 2011 10:24 AM

    stephenadams1's Avatar

    Ah, that's a lot clearer now, thanks Matt. To add and remove children views
    your using addChild() and removeChild() events?

    Stephen

    On 29 March 2011 11:14, matt <
    [email blocked]> wrote:

  11. 11 Posted by matt on 29 Mar, 2011 10:28 AM

    matt's Avatar

    yeah, ScreenManagerView essentially just adds and removes children when it's
    told to, it has no knowledge of RobotLegs or anything else, apart from the
    screens that it is adding and removing, and all it knows about them is that
    they are DisplayObjects.

    On 29 March 2011 11:24, stephenadams1 <
    [email blocked]> wrote:

  12. 12 Posted by stephenadams1 on 29 Mar, 2011 10:30 AM

    stephenadams1's Avatar

    Awesome, I get it now. Thanks Matt.

    On 29 March 2011 11:28, matt <
    [email blocked]> wrote:

  13. 13 Posted by Abel de Beer on 29 Mar, 2011 10:34 AM

    Abel de Beer's Avatar

    In my opinion a screen manager is not necessary when using the Mediator pattern as it is applied in Robotlegs. Every Mediator can decide how to handle the adding and removing of the view component it is mapped to. This means you can also handle 'add' and 'remove' animations (etc.), by listening for the appropriate events.

    @stephenadams1: could you please try to rephrase WHY you think you need a global screen manager? What is your main problem?

  14. 14 Posted by Abel de Beer on 29 Mar, 2011 10:37 AM

    Abel de Beer's Avatar

    One more note:

    In general it's smart to follow the Single Responsibility Principle, which means you should try to have every part of your application function on its own. This way you stay very flexible when other parts change. Using a global controller (like your screen manager example) goes against this principle.

  15. 15 Posted by stephenadams1 on 29 Mar, 2011 10:44 AM

    stephenadams1's Avatar

    Hi,

    The problem I'm trying to solve is I'm building a 'smart ad', like a simple
    banner ad but is more intelligent. This ad unit, that's what they are
    called, starts up and makes a http request to see if some information is
    available. While its making that request it should show a 'loading' view.
    Then when the data is returned, switch to a data loaded view. It should do
    this automatically without any user prompting. The data request can also
    return no data so the ad should switch to a no data loaded view, where it
    will display a image.

    So this ad unit will have at least 3 views, in later versions it may support
    other views, for example to show a view that contain images/text that is
    more appropriate to the user viewing the ad.

    This is the problem I'm trying to solve.

    Stephen

    On 29 March 2011 11:38, Abel de Beer <
    [email blocked]> wrote:

  16. 16 Posted by matt on 29 Mar, 2011 10:50 AM

    matt's Avatar

    interesting point. i look at it as my ScreenManager having the single
    responsibility of displaying/removing screens, leaving my screens to
    concentrate on their responsibilities without worrying about how they are
    displayed/removed. The screens know nothing about the manager, the manager
    knows nothing about the screens. Whereabouts do you control the actual
    addition and removal of views like this?

    On 29 March 2011 11:38, Abel de Beer <
    [email blocked]> wrote:

  17. 17 Posted by Abel de Beer on 29 Mar, 2011 01:34 PM

    Abel de Beer's Avatar

    For Stephen's example, here's the route I would take:

    1. Context: make injector mappings, including startup command, but do not add the views to the context view yet.

    2. Startup command: run your http request and add a new instance of the 'loading' view to the contextView. Map commands to your http service's 'complete' and 'failed' events. Your 'loading' mediator also needs to listen to these events, so that it can do view.parent.removeChild(view), which will trigger the onRemove() method of the mediator.

    3. a. Data loaded command: add a new instance of your 'data loaded' view to your contextView. This will trigger your 'data loaded' mediator's onRegister() method, which can initialize its view component using the loaded data.

    4. b. No data command: add a new instance of your 'no data' view to your contextView.

    That should do the trick! If you want I could provide you with a simple code example. Let me know.

  18. 18 Posted by stephenadams1 on 29 Mar, 2011 01:47 PM

    stephenadams1's Avatar

    Hi Abel,

    Thanks for this very helpful, one question I have is the
    view.parent.removeChild(view) line, are you passing the current view to its
    parent to remove itself, so if I have a view called userDetailsPanel I'd do
    this:

    userDetailsPanel.parent.removedChild(userDetailsPanel);

    This works?

    Stephen

    On 29 March 2011 14:34, Abel de Beer <
    [email blocked]> wrote:

  19. 19 Posted by Abel de Beer on 29 Mar, 2011 02:34 PM

    Abel de Beer's Avatar

    It's a logical question, because it's the most confusing line. ;) I don't like the syntax that much, but it works as intended. Here's what I meant:

    class LoadingViewMediator extends Mediator
    {
        [Inject] public var view:LoadingView;
    
        override public function onRegister():void
        {
            eventMap.mapListener(eventDispatcher, HttpServiceEvent.COMPLETE, onHttpServiceComplete);
        }
    
        private function onHttpServiceComplete(event:HttpServiceEvent):void
        {
            view.parent.removeChild(view);
        }
    
        override public function onRemove():void
        {
            eventMap.unmapListener(eventDispatcher, HttpServiceEvent.COMPLETE, onHttpServiceComplete);
        }
    }
    

    First you inject the instance of the view class that this Mediator is mapped to and that was just added to the contextView. You then start listening for the HttpServiceEvent.COMPLETE event - this is just an example name of course, you should create your own custom event classes for this. Inside the onHttpServiceComplete() method you remove the view component from the contextView: view.parent actually references the contextView! This way the onRemove() method is eventually called automatically.

    Note: doing unmapListener() in the onRemove() method is actually unnecessary, because the Mediator's preRemove() method automatically unmaps all eventMap listeners. This is just for the sake of the example.

    I hope it's more clear now. :)

  20. 20 Posted by stephenadams1 on 29 Mar, 2011 02:50 PM

    stephenadams1's Avatar

    Yes it is, thanks

    Stephen

    On 29 March 2011 15:35, Abel de Beer <
    [email blocked]> wrote:

  21. Support Staff 21 Posted by creynders on 29 Mar, 2011 05:24 PM

    creynders's Avatar

    Sorry Abel, but I'd really advise against using this approach. There are a number of problems with it.

    • What if you need to perform an action when the service has completed it's call, but BEFORE the view receives the data? You'll have to refactor your mediator, because it's indirectly coupled tightly to the service

    • What if you want to reuse a view for something else? You'll be adding more and more service-coupled events to the mediator?

    • What if you want to add out-in transitions? Again, refactoring. Lot's of it probably.

    • A view should never be responsible for removing itself, since it's container won't be able to react to it BEFORE it's removed.

    Views shouldn't know/care how/when they are shown or not, because if you need to change anything in that sequence you'll be refactoring a lot.

  22. 22 Posted by Abel de Beer on 29 Mar, 2011 05:55 PM

    Abel de Beer's Avatar

    @creynders: thanks for the feedback, I think it's a nice discussion. :) So here's my feedback on your points:

    The idea behind a Mediator is that it functions as a bridge between a view component and the rest of the application. This means that there will always need to be some form of coupling, either through events, signals, or even by injecting models or services (which is generally a bad idea of course). By applying the Mediator pattern properly you assure that your view components become reusable, single responsibility objects. My point is that: YES, obviously you'll need to refactor your Mediator once the goal / behavior of the assigned view component changes, because it needs to be able to properly communicate with the rest of the application.

    So, regarding point 1 & 2: if you don't want the coupling between your Mediator and "service coupled events", then create "view related events" that are dispatched from commands in response to the events dispatched by your service.

    Regarding point 3: I do admit that this is a hassle. But still, if your views know which transition to use when they need to be added or removed, they can all respond to the corresponding events that trigger this behavior.

    Could you give an example where your fourth point of feedback can cause trouble? I can imagine situations where this would be bothersome, but I just can't think of a concrete example. :)

    Please note that in my descriptions above the view components AND mediators never keep track of their display state. They just respond to events and act accordingly.

    In general I don't like the idea of a God-like controller (like the screen manager idea), because that means it will likely become a very inflexible object: you will need to make changes to it every time you decide to alter the behavior of your application. If your view/mediator combinations 'decide' for themselves whether they need to respond to certain events you will be a lot more flexible, in my opinion.

    Still, the fun thing about Robotlegs is that (almost) anything is possible, and anyone should decide for themselves which approach to use. ;)

  23. Support Staff 23 Posted by creynders on 30 Mar, 2011 08:28 AM

    creynders's Avatar

    I totally agree with your dislike for a god-like controller and to avoid it, but that's not what I'm suggesting either. The difference is that a god-like controller would manage ALL display object addition and removal, what is exactly the opposite of my suggestion. And maybe I should've remarked above that many times the whole navigating and transitioning pages-part is completely outside the framework and simply a component used by a view.

    To me (and that's an idea I picked up from Stray) a mediator should only pass data and events between its view and the framework. Nothing more. It should certainly not be responsible for removing the view, since then you're putting presentation logic into it.

    As for

    obviously you'll need to refactor your Mediator once the goal / behavior of the assigned view component changes

    That's exactly it, you're NOT changing the goal of the assigned view component, yet you have to refactor its mediator. That's why it's indirectly coupled tightly to the service. You change business logic, yet you have to refactor your mediator, that defeats the purpose.

    As for point 4, a simple concrete example does not come to mind, but I've encountered it a few times, that I needed to let the container react to one of it's child components removals before it happens. I'll browse a few of my latest projects and see if I encounter such a situation (that doesn't need 10 pages of explanation as to why in that specific case it's necessary)
    Point is, it's better to prevent than to cure, that's what best practices, frameworks et cetera are for, to give you systems and methodologies that help you keep things reusable, scalable and maintainable.

    And yes, it's nice to discuss these things, I learn so much by doing this.

  24. 24 Posted by matt on 30 Mar, 2011 08:54 AM

    matt's Avatar

    regarding the god like controller - i was not suggesting something that
    handles all display objects - i'm suggesting that if there's a single area
    of the screen that needs to display one of a selection of views and be able
    to change between them (it could be a main content window, popup windows, an
    interface panel, whatever, then there would be a screen manager view would
    handle the addition and removal of this type of view, along with any
    transitions. If there were different types of view components you wanted to
    manage in this way for different parts of the application then you would
    have a different screen manager view to manage each of these different types
    of view. Perhaps I should call it a view component display manager rather
    than a screen manager!

    I think this is pretty much what creynders was originally suggesting, and to
    be honest, i can't think of another way to handle this, but i'm all ears if
    there's a better way!

    On 30 March 2011 09:28, creynders <
    [email blocked]> wrote:

  25. Stray closed this discussion on 11 May, 2011 08:57 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