How do I manage mediators for Parent-Child views?

mike's Avatar

mike

26 Jun, 2013 11:12 AM

I'm playing around with RL2 and looking at how to setup parent-child views and have a noob question...

In flash IDE I have a Gameboard MC in which I've dragged a Counter MC. In this counter I have a textfield with instance name "myText"

The gameboard and counter both have mediators - I'm not sure how I reference the TextField - for eg. do I need to listen for when the textfield is added to the stage in the Counter Mediator or can I access this via contextView?

Is there a special practice for dealing with Parent-Child views..or does each view simply get its own mediator?

Thanks!

  1. Support Staff 1 Posted by Ondina D.F. on 27 Jun, 2013 07:50 AM

    Ondina D.F.'s Avatar

    Hi Mike,

    Not sure if I understand your question correctly. Would you care to elaborate on that?

    If you mapped your views to mediators like so:

    mediatorMap.map(GameBoardView).toMediator(GameBoardMediator);
    mediatorMap.map(CounterView).toMediator(CounterMediator);
    

    The GameBoardMediator and CounterMediator will be automatically created after the views have been added to the stage.

    If you want to access the text field within your CounterMediator, you do:

    view.myTextField.text =“some value“;

    The GameBoarderMediator should not be allowed to have direct access to subcomponents of its view (GameBoarderView), like this: view.counterView.myTextField.

    Instead, GameBoarderMediator should call a method (public API) on its view:

    view.doSomethingWithTheSubView(someProperty);

    And in GameBoarderView:

    public function doSomethingWithTheSubView(someProperty:someType):void
    {
        counterView.doSomethingWithSomeProperty(someProperty);
    }
    

    But, since both views are mediated, they should communicate with each other through their mediators, via events. If myTextField has changed, CounterView dispatches an event, and CounterMediator,which is listening to that event redispatches it. GamesBoarderMediator, or any other actor in your app that registered an event listener, can react to it, if need be.

    If components weren’t mediated, an easy to remember rule is:

    • Parents communicate with children via children’s API.

    [ Parent->FirstChild.someMethod()->SecondChild.anotherMethod() ]

    • Children communicate with parents via events.

    [ Child.dispatchEvent(some event)->Parent listens->Parent handles event ]

    It is not clear to me what you mean by:

    I'm not sure how I reference the TextField - for eg. do I need to listen for when the textfield is added to the stage in the Counter Mediator or can I access this via contextView?

    Why do you need the contextView to access myTextField?
    If you want to access something from the contextView, you need to do this:

    contextView.view.someProperty
    or
    contextView.view.someMethod(),
    where view is the actual display object.

    Ondina

  2. 2 Posted by mike on 27 Jun, 2013 11:56 AM

    mike's Avatar

    Hi Ondina,

      Fantastic answer :) That's cleared up a lot of things for me. If I may
    impose a bit more I have this set in the Counter Mediator

            public function CounterMediator()
            {
                view.textField.text ="some value";
            }

    I imported a swc from Flash, with automatically declare stage instances
    ticked - yet the field is still empty. Can you see what I may have missed?

    Thanks again for your info

    Cheers

    Michael

  3. Support Staff 3 Posted by Shaun Smith on 27 Jun, 2013 12:22 PM

    Shaun Smith's Avatar

    Hi Michael,

    You need to wait until the Mediator has been registered before attempting to access the injected view. You can do this by overriding the initialize() method:

    override public function initialize():void  
    { 
      view.textField.text ="some value"; 
    }
    

    Hope that helps!

  4. 4 Posted by Mike Wheelaghan on 27 Jun, 2013 01:02 PM

    Mike Wheelaghan's Avatar

    Hi Shaun many thanks for your reply !

    If I add a counter separately to the contextView eg

                var gb:Gameboard = new Gameboard();
                var counter:Counter = new Counter();
                
                contextView.view.addChild(gb);
                contextView.view.addChild(counter);

    The CounterMediator fires and sets the text fine. What I would like is to preserve the design of the Gameboard in which this Movie Clip contains a Counter symbol at a particular x, y. I have an Counter MovieClip named "myCounter" in the fla file

    Then I try something like this in GameboardMediator - do you know if this is possible the way I describe?

            override public function initialize():void
            {
                view.myCounter.setText("testtset");
            }

    Thanks again :)

    Mike

  5. Support Staff 5 Posted by Shaun Smith on 27 Jun, 2013 01:15 PM

    Shaun Smith's Avatar

    Hiya,

    You don't need to add them to the ContextView separately in order for them to be mediated. Robotlegs will pick up on any views that you have mapped when they land on stage. In other words, if Gameboard contains an instance of Counter, you can just add a Gameboard to the stage. Usually, you only interact with the ContextView at startup, where you add a single "main view" to the stage, any sub views that have been mapped will be picked up and mediated. Let me know if that clears things up a bit.

  6. 6 Posted by Mike Wheelaghan on 27 Jun, 2013 02:07 PM

    Mike Wheelaghan's Avatar

    Thanks a lot for that Shaun and Ondina - managed to get it working with your help

    Cheers!

    Mike

  7. Support Staff 7 Posted by Ondina D.F. on 27 Jun, 2013 02:26 PM

    Ondina D.F.'s Avatar

    Mike, the pleasure is ours :) Good to hear you got it working!

    You can re-open this discussion, if you get stuck again and/or open new discussions for new issues.

    See you around,
    Ondina

  8. Ondina D.F. closed this discussion on 27 Jun, 2013 02:26 PM.

  9. mike re-opened this discussion on 28 Jun, 2013 10:08 AM

  10. 8 Posted by mike on 28 Jun, 2013 10:08 AM

    mike's Avatar

    Hi Guys, if I may just add one more question - I've noticed the CounterMediator doesn't seem to activate when its an instance variable inside Gameboard, but does if I add separately to the ContextView. If I understand Shaun's notes correctly this myCounter instance variable inside Gameboard would startup its own CounterMediator?

    thanks so much for all your help so far :)

    M

        public function get _counter():Counter { return this["myCounter"]; }
    
  11. Support Staff 9 Posted by Ondina D.F. on 28 Jun, 2013 12:30 PM

    Ondina D.F.'s Avatar

    Hi Mike,

    You have to create an instance of CounterView and then add it to GameboardView.

    In GameboardView:

    private var counterView: CounterView;
    private function gameboardOnAddedToStage():void
    {
        counterView = new CounterView();
        addChild(counterView);
    }
    
    After counterView is added to the stage, its mediator will be created automatically.

    Is your project a pure as3 one or Flex? How are you initializing your context?

    Could you attach a sample app? I’m willing to take a look at it and tell you how to add your views and so on. It might be easier to see why it is not working for you.

    Ondina

  12. 10 Posted by Mike Wheelaghan on 28 Jun, 2013 02:08 PM

    Mike Wheelaghan's Avatar

    Thanks again Ondina ! Here's my effort so far - Im attempting to preserve the flash ide design, so in case I have a designer draw a gameboard with a counter at a specific location and several other movie clips in flash - that if I reference them by name I can preserve their position in the scene (otherwise if I addChild() I would need explicitly set x, y..or am I mixed up about that?)

    cheers :)

  13. Support Staff 11 Posted by Ondina D.F. on 28 Jun, 2013 02:56 PM

    Ondina D.F.'s Avatar

    I've dl-ed it. I'll be back...

  14. Support Staff 12 Posted by Ondina D.F. on 28 Jun, 2013 03:20 PM

    Ondina D.F.'s Avatar

    Here the changes:
    TextX.as:

    public class TestX extends Sprite
    {
        
        private var _context:IContext;
        
        public function TestX()
        {
            _context = new Context()
                .install(MVCSBundle, SignalCommandMapExtension, ViewProcessorMapExtension)
                .configure(AppConfig)
                .afterInitializing(init)
                .configure(new ContextView(this));
        }
        
        private function init():void
        {
            var gb:Gameboard = new Gameboard(); 
            addChild(gb);       
        }
    }
    

    Note the line .afterInitializing(init).

    AppConfig:

    [Inject]
        public var mediatorMap:IMediatorMap;
            
        public function configure():void
        {
            
            mediatorMap.map(ICounter).toMediator(CounterMediator);
            mediatorMap.map(IGameboard).toMediator(GameboardMediator);
        }
    }
    

    CounterMediator

    override public function initialize():void
    {
        trace("******   CounterMediator *****");
        //view.textField.text ="some value";
        view.setText("some value");
    }
    

    Something is wrong with _myTf, but I can’t see the base classes, so I don’t know exactly why myTf is not working. Thus I created another TextInput in the constructor of the Counter:

    private var someText:TextInput;
    public function Counter()
    {   
        someText= new TextInput();  
        addChild(someText);
    }
    public function setText(val:String):void
    {
        someText.text = val;
    //  _myTf.text = val;
    }
    

    Gameboard

    public function Gameboard()
    {
        addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
        /*var c:Counter = new Counter();
        addChild(c);*/
    }
    
    protected function onAddedToStage(event:Event):void
    {
        var c:Counter = new Counter();
        addChild(c);
    }
    

    It’s working fine so far:)

    It would be good if I could see the base classes as well.
    I’ll attach the changed project later on.

  15. Support Staff 13 Posted by Ondina D.F. on 28 Jun, 2013 03:36 PM

    Ondina D.F.'s Avatar

    I forgot to say that I used robotlegs-framework-v2.0.0 in your project.
    (http://www.robotlegs.org/)

  16. Support Staff 14 Posted by Ondina D.F. on 28 Jun, 2013 04:27 PM

    Ondina D.F.'s Avatar

    Ok, it works with the base text field as well. I didn’t see it, because Counter was not fully on screen.

    So, var c:Counter = new Counter(); c.x=150; addChild(c);
    solves the problem, haha.

    Then you can set the text like this:

    In Gameboard (it should implement an interface having setText):

    public function setText(value:String):void
    {
        myCounter.textField.text = value;
    }
    

    In Counter

    public function setText(value:String):void
    {
        textField.text = value;
    //or
    // _myTf.text = value;
    }
    

    The names of the text fields are kind of confusing :)

    I’m attaching the project with my changes.

  17. Support Staff 15 Posted by Ondina D.F. on 28 Jun, 2013 04:39 PM

    Ondina D.F.'s Avatar

    Im attempting to preserve the flash ide design, so in case I have a designer draw a gameboard with a counter at a specific location and several other movie clips in flash - that if I reference them by name I can preserve their position in the scene (otherwise if I addChild() I would need explicitly set x, y..or am I mixed up about that?)

    Hmm, I don’t know how to answer that.

    If you set Counter’s position, size etc inside Counter’s constructor, they are preserved when you add it in Gameboard, and of course you don’t need to set them again in Gameboard. But, I’m afraid I don’t understand what you mean :)

  18. Support Staff 16 Posted by Shaun Smith on 28 Jun, 2013 04:50 PM

    Shaun Smith's Avatar

    I’m afraid I don’t understand what you mean

    I haven't had a chance to look at the sample yet, but this is my understanding:

    1. Use Flash IDE to design a composite/nested view
    2. Map mediators to the various individual view instances within that view
    3. Add the composite view to the stage
    4. Mediators should be created for each mapped instance

    This is basically what RL was designed for. I'm not sure why it isn't working for you, but it really should. It would suck to have to lay everything out with code!

    I can't look into it right now (I'm still at work), but here are some things to double check:

    1. All classes that you map to mediators are set for export in the Flash library panel.
    2. The instances on stage have valid instance names (actually, this shouldn't matter)
    3. You map them before you add the view to the stage
    4. You have selected "Export SWC" in the project properties.

    Will try to take a look later / over the weekend.

  19. 17 Posted by Mike Wheelaghan on 29 Jun, 2013 05:39 AM

    Mike Wheelaghan's Avatar

    Hi - many thanks again guys for all your patience :-)

    Shaun is exactly right - that's what I'm trying to achieve. Have Flash IDE to design a composite nested view and map separate mediators to individual instances within that view.

    Ondina thanks very much for your example I can see now that using addChild() is a guarenteed way to activate the child mediator (Counter) . My eventual goal is what Shaun describes ie.

    The package.GameboardBase and package.CounterBase are the linkage names I provided in the Export settings in Flash IDE so all the code is in flashbuilder. I export a single swc and import into FB

    Where I'm trying to get to is

                viewProcessorMap.map(ICounter).toProcess(new MediatorCreator(CounterMediator));
                viewProcessorMap.map(IGameboard).toProcess(new MediatorCreator(GameboardMediator));

                var gb:Gameboard = new Gameboard();
                contextView.view.addChild(gb);

    Since Gameboard contains a reference to Counter via

            public function get _myTf():Counter
            {
                return this["myCounter"]; //myCounter is the name of the Counter Symbol that is placed inside the Gameboard Symbol
            }
    and we can set the text of the Counter via the GameboardMediator, I'm hoping it's possible to have this instance of Counter mediated. (which has specific layout and keyframe location within the Gameboard Movie Clip )

    The reason for all this is that it would achieve a clean separation between designer and developer and no matter how complex the nested/composite view gets - you can have as many sub-views as you like , each view is mediated and logical separate from the parent.

    Hope to get your thoughts on this - appreciate the sample code again

    M

  20. Support Staff 18 Posted by Ondina D.F. on 29 Jun, 2013 09:16 AM

    Ondina D.F.'s Avatar

    Well, Shaun seems to have understood better what you need to achieve :)

    Yes, I could see CounterBase and GameboardBase in the referenced libraries. CounterBase has a property textField:TextInput and GameboardBase has myCounter:CounterBase.

    My confusion was about your interfaces and the way you’ve mapped them. It wasn’t clear what exactly needed mediation.
    CounterBase and GameboardBase don’t implement any interfaces, so the mapping to ICounter won’t work, unless you add the view implementing ICounter to the stage.
    Of course you don’t need to add the CounterBase again, if it was already added by Gameboard, but if you have these mappings:

    mediatorMap.map(ICounter).toMediator(CounterMediator);
    mediatorMap.map(IGameboard).toMediator(GameboardMediator);
    

    and you add gameboard like this

    var gb:Gameboard = new Gameboard(); 
    contextView.view.addChild(gb);
    

    and you don’t add Counter too, only GameboardMediator will be created.

    If you map them like this:

    mediatorMap.map(CounterBase).toMediator(CounterMediator);
    mediatorMap.map(IGameboard).toMediator(GameboardMediator);
    

    and in CounterMediator you inject CounterBase, both mediators get created, and you don’t need to add Counter manually. That means, that the mediator for a subcomponent from your swc will be created, if it is mapped correctly.

    I think you need to let CounterBase implement ICounter if you want to keep your original mappings.
    Maybe I’m wrong, though.

  21. 19 Posted by mike on 29 Jun, 2013 09:55 AM

    mike's Avatar

    Thanks Ondina that really helped. If I use CounterBase that works , I can access the textfields directly from both mediators - In practice I will have a CounterUpdate command sending to a CounterMediator and so forth...I definitely need to think more about the view architecture but at least I know that it can be done no worries, thanks heaps for helping me out !!

    M

  22. Support Staff 20 Posted by Ondina D.F. on 30 Jun, 2013 06:46 AM

    Ondina D.F.'s Avatar

    It's nothing!

  23. Ondina D.F. closed this discussion on 09 Jul, 2013 01:44 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