Possible Bug with Mediator when using Flex States

Phil Chung's Avatar

Phil Chung

27 Jan, 2010 03:27 AM

I have set up a simple example to show the behavior. When views are attached to Flex states, switching between states causes the associated mediator to get created every time you switch to a state that contains one of the views, even if that mediator was previously constructed. The expected behavior is that the mediator will only be created the first time a state is displayed, and thereafter, that mediator instance will be used for that view. The view is not created again, so this works properly.

Any assistance is appreciated as this is being used currently in a project. Thanks.

  1. 2 Posted by levi.strope on 27 Jan, 2010 05:26 AM

    levi.strope's Avatar

    My reply is more of a question than an answer so please forgive me.

    I was thinking that in your context, you could try the following. Will injector.mapSingleton() work to satisfy your request? Anyone from the RL crew? I'm drawing straws as it's late and this was the first thing that came to mind.

        override public function startup():void {
            // Map Views
            injector.mapSingleton(ViewState1);
            mediatorMap.mapView(ViewState1, ViewState1Mediator);
            mediatorMap.mapView(ViewState2, ViewState2Mediator);
    
            dispatchEvent(new ContextEvent(ContextEvent.STARTUP_COMPLETE));
        }
    
  2. 3 Posted by darren on 27 Jan, 2010 06:20 AM

    darren's Avatar

    Phil. Just playing around quickly, I've found that in the MediatorMap Class of RB, it will call createMediator under certain conditions.

    If you turn off the autoCreate (which in your case i believe is acceptable since your defining the mediator), you won't have the constructor called every time. (assuming I recall the purpose of autoCreate correctly).

    Try this out, seems to trace out fine here.

    mediatorMap.mapView(ViewState1, ViewState1Mediator, null, false);
    mediatorMap.mapView(ViewState2, ViewState2Mediator, null, false);

    Hope that helps your project :D

  3. Support Staff 4 Posted by Till Schneidereit on 27 Jan, 2010 11:04 AM

    Till Schneidereit's Avatar

    Hi Phil,

    you should probably turn off autoRemove when mapping your views.

    As Darren wrote, you can also turn off autoCreate, but in your case,
    you'd probably want the mediator to get created the first time a view
    is activated through a state change. So, modifying Darrens example,
    your code could look like the following:

    mediatorMap.mapView(ViewState1, ViewState1Mediator, null, true, false);
    mediatorMap.mapView(ViewState2, ViewState2Mediator, null, true, false);

  4. Support Staff 5 Posted by Shaun Smith on 27 Jan, 2010 11:06 AM

    Shaun Smith's Avatar

    Hi Phil,

    Automatic mediator registration works by listening for ADDED_TO_STAGE events on the contextView - it's the only way we can find out the scope of the view component. Changing states causes the views associated with those states to be added/removed from the display list, in turn registering/removing the mediators.

    This is actually the expected behavior. Even though the view components are only constructed once, when they are removed from stage we can no longer consider them inside the scope of the contextView - when a view component is removed from the display list we have no guarantee that it will be added back to the display list in the same scope (it might be added somewhere else, inside a different scope/context).

    @levi - unfortunately, the singleton mapping will be overridden by a temporary mapping in the MediatorMap when the mediator is created, and hence lost.

    @darren - manual registration/removal is indeed the solution here, but I would recommend leaving autoCreate on and turning autoRemove off, like so:

    mediatorMap.mapView(ViewState1, ViewState1Mediator, null, true, false);
    mediatorMap.mapView(ViewState2, ViewState2Mediator, null, true, false);
    

    It's worth noting that it then becomes the developers responsibility to remove the mediators when appropriate.

    @phil A better approach might be to avoid mapping mediators to view components that are toggled by state and to instead mediate the view component that holds them. In your case, I would put ViewState1 and ViewState2 inside another view component, and map a mediator to that instead.

  5. 6 Posted by Phil Chung on 27 Jan, 2010 04:20 PM

    Phil Chung's Avatar

    Thanks for the suggestions guys! I set autoRemove to false and that seems to do the trick.

    Shaun, thanks for the explanation of what's going on behind the scenes. In terms of mapping one mediator to a view that contains ViewState1 and ViewState2, personally, I think it makes more sense to seperate the logic for ViewState1 and ViewState2 into seperate mediators because they really are unrelated and so logic for each should be decoupled.

    I'd be interested to hear how other people are handling this scenario because this type of thing occurs not only when you use states, but any time you use navigators like Accordion, TabBar, etc. Do you guys generally just set autoRemove to false?

  6. Support Staff 7 Posted by Shaun Smith on 27 Jan, 2010 04:48 PM

    Shaun Smith's Avatar

    Hi Phil,

    As far as I know Accordions, ViewStacks, Lists etc use deferred instantiation - they don't actually remove things from the display list, creation is simply deferred until needed - so everything should work as expected.

    Personally, I don't ever set autoRemove to false. I treat mediators as temporary link-ups between the framework and on-display view components. IE, I expect my mediators to exist only while their view components are on-stage. I also expect to get a new mediator instance when a view component is added to the stage after previously having been removed.

    If ViewState1 and ViewState2 are indeed totally unrelated, then the current behavior seems desirable to me - each ViewState should only be mediated while on-stage. I feel that perhaps your mediators are stateful, or trigger off some kind of action on registration, and hence cause undesirable side-effects. Could you provide some more detail as to why you find the behavior undesirable?

  7. 8 Posted by Phil Chung on 27 Jan, 2010 05:32 PM

    Phil Chung's Avatar

    Shaun,

    Thanks for taking the time to respond. Perhaps I misunderstood the behavior of Mediators as I didn't realize they weren't stateful, but your explanation makes sense.

    So I'd like to ask how you would set up this scenario. I have 3 views, each displayed in a different Flex state. When each view is first displayed, I need to make a call(s) on the service class and listen for the event that tells the mediator that the data is ready, and we can update the view. However, after that view is displayed once, each subsequent time you display that view, we don't want to repeat those calls on the service. The problem arose for me because I was making the service calls and setting up to listen for events in the onRegister of my Mediators.

    It's sounding more to me like I need to set up PresentationModels for my views, since that's not what Mediator is meant for, but I'd still like your opinion, particularly on where you think the service calls should be triggered.

  8. Support Staff 9 Posted by Joel Hooks on 27 Jan, 2010 07:35 PM

    Joel Hooks's Avatar

    The initial service call can be a Command that is triggered by an event (possibly the event that displays the view initially). The Command will check the model, if the data is there, it will make the service call. The service updates the Model via a separate Command (or directly, I prefer to use Commands here). When the model's data is updated it fires an event that the mediator(s) listen for and know to update their view components with the new data.

    I always encapsulate service actions in Commands. That is really what they are good for, those cross tier actions. Any data that a service returns should be stored on the model, so a mediator should listen for service events. Mediators will listen for events from other mediators, models, and commands.

    This would be a good addition to best practices documentation I think...

  9. 10 Posted by Phil Chung on 27 Jan, 2010 09:15 PM

    Phil Chung's Avatar

    Thanks for chiming in Joel. The way you explained is pretty close to how I'd set it up, except that I was calling the command (triggering the event) inside of the mediator. I'll move it outside of the mediator as you suggest.

    Initially my perspective on the mediator is that it's basically code behind, and aside from the injection stuff, which is awesome, it allows me to keep the code out of my views. Which is why I was thrown off when new instances of mediator were being created for the same view instance.

    Overall though, I think the best practices doc is fantastic. Lots of good info in there.

  10. Joel Hooks closed this discussion on 28 Jan, 2010 08:14 PM.

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