Events being caught intermittently

John R. Nyquist's Avatar

John R. Nyquist

17 Sep, 2012 11:55 PM

I'm new to Robotlegs, so I'm not sure if I'm missing something. I have a view that has a child which is dispatching events with bubbling on. The mediator for the view is only occasionally catching the events dispatched by the child of the view. The behavior is not consistent between runs of the program. I have overridden the clone() function for the event. Any help would be appreciated.

Regards,
John

  1. Support Staff 1 Posted by Ondina D.F. on 18 Sep, 2012 07:14 AM

    Ondina D.F.'s Avatar

    Hi John,

    What kind of a component is the child of the View? Is it, by any chance, a List or a DataGrid using an ItemRenderer? ItemRenderers are known for their unpredictable behavior. That’s correct, you should let the event bubble and override clone. But instead of listening directly on an ItemRenderer in the Mediator, the View, the parent component of the List or DataGrid, should handle the event dispatched by its child and then re-dispatch a custom event (with a payload) to its Mediator.

    Is the parent component able to hear the event every time the subcomponent dispatches it? If the answer is no, then the issue you’re encountering has nothing to do with robotlegs.

    Are you keeping a reference to the context?
    http://knowledge.robotlegs.org/kb/reference-mvcs-implementation/kee...

    If this doesn’t answer your question, is it possible to post some code or attach a simple application reproducing the issue?

    Cheers,
    Ondina

  2. 2 Posted by John R. Nyquist on 18 Sep, 2012 02:32 PM

    John R. Nyquist's Avatar

    Hi Ondina,

    Thanks for the response. This is an AS3 project. The child of the view is a class that extends flash.display.Sprite.

    Yes, the parent is able to hear the event each time the subcomponent dispatches it.

    Yes, I'm keeping a reference to the context.

    The code involved seems to be basics...
    I use mediatorMap.mapView to map the view to the mediator
    The mediator uses addViewListener to listen for the event
    contextView.addChild to add the view to the main context
    The child dispatches the event, bubbling true
    The event passes its parent and is caught by the mediator (sometimes)

    I'm using the robotlegs swc. Maybe I should download the source and attempt to step through.

    Regards,
    John

  3. Support Staff 3 Posted by Ondina D.F. on 18 Sep, 2012 03:05 PM

    Ondina D.F.'s Avatar

    Hey John,

    If you’re going to link against the source, you should know about this (maybe you already do):

    https://github.com/robotlegs/robotlegs-framework/wiki/Common-Proble...

    The mediator uses addViewListener to listen for the event contextView.addChild to add the view to the main context

    Not sure what you mean. It sounds a bit unusual.

    Can you post at least the code where you are mapping the event listener inside your mediator, and where you’re dispatching the event from the view? Is the event dispatched from the child component triggered by user interactions or something else? What's going on inside your sub-component?
    If you try to let the parent listen for the child’s event and then redispatch it within an event handler, can the mediator catch it?

    Without seeing some code it’s really hard to guess what’s going on in your app, but I think the issue has something to do with the way you are mapping the event or with some additional logic you have in your mediator’s onRegister() , or with the component itself.
    I’m pretty sure it’s not robotlegs’ fault ;)

    Ondina

  4. Support Staff 4 Posted by Ondina D.F. on 18 Sep, 2012 03:47 PM

    Ondina D.F.'s Avatar

    Here, an example of a ParentViewMediator listening for a custom event, dispatched by ParentView within an event handler for an event dispatched by its ChildView.

    ParentView:

    private function init():void
    {
    var childView:ChildView = new ChildView();
    addChild(childView);
    childView.addEventListener(IndexChangeEvent.CHANGE, childViewEventHandler);
    }
    
    private function childViewEventHandler(event:IndexChangeEvent):void
    {
     //someData = event.currentTarget.selectedItem
    dispatchEvent(new SomeEvent(SomeEvent.SOMETHING_HAPPEND_WITHIN_THE_CHILDVIEW, someData)); 
    }
    

    ParentViewMediator:

    override public function onRegister():void
    {
    addViewListener(SomeEvent.SOMETHING_HAPPEND_WITHIN_THE_CHILDVIEW, onSomethingHappened, SomeEvent);
    }
    
  5. 5 Posted by John R. Nyquist on 18 Sep, 2012 04:27 PM

    John R. Nyquist's Avatar

    Thanks for the link. When I mentioned using the source to step through, I did not intend to imply it might be robotleg's fault. I'm new to robotlegs and I thought stepping through might give me insight into where I may be confusing the framework.

    Regarding code...
    In the child of the view:

         private function statusHandler(event : NetStatusEvent) : void {
                switch (event.info.code) {
                    case "NetStream.Play.Stop":
                        dispatchEvent(new VideoEvent(VideoEvent.VIDEO_ENDED, true));
                        break;
                }
            }
    

    In the mediator:

         override public function onRegister() : void {
                addViewListener(VideoEvent.VIDEO_ENDED, onVideoEnded, VideoEvent);
            }
            
            private function onVideoEnded(e:VideoEvent):void
            {
                //turn view event to application event
                dispatch(new DecisionEvent(DecisionEvent.SHOW_ACTIONS) );
            }
    
  6. Support Staff 6 Posted by Ondina D.F. on 18 Sep, 2012 05:05 PM

    Ondina D.F.'s Avatar

    When I mentioned using the source to step through, I did not intend to imply it might be robotleg's fault.

    Hehe, ok.

    Is your child view always on stage, or are you removing it after it has dispatched an event?

  7. Support Staff 7 Posted by Ondina D.F. on 18 Sep, 2012 05:44 PM

    Ondina D.F.'s Avatar

    The code you posted looks ok, but it’s really better to let the parent view handle the child’s event and re-dispatch it as I showed you in a previous post. I'm out of time for today, it’s evening here, where I live, so I can’t get into more details...

    All I can think of right now are some components re-parenting issues ( ‘The mediator uses addViewListener to listen for the event contextView.addChild to add the view to the main context’ ), changing states, asynchronous processes…
    If you want, you can attach your app. I’m willing to take a look at it. I can make the discussion private, download the code, delete the attachment, and mark it as public again, if, for some reason, you want to hide it from the public eye. Btw, you can mark it as private as well.
    I’ll be back tomorrow with more answers, hopefully. Maybe others will have more suggestions too.

  8. 8 Posted by John R. Nyquist on 18 Sep, 2012 10:48 PM

    John R. Nyquist's Avatar

    Thanks for your help, Ondina, have a good evening. I'll go ahead an turn bubbling off, have the parent view catch it, and then re-dispatch it and see how that goes.

  9. Support Staff 9 Posted by Ondina D.F. on 19 Sep, 2012 02:13 PM

    Ondina D.F.'s Avatar

    You’re welcome, John. Hopefully, we’ll find out what exactly is causing the issue you have faced.
    Sometimes it’s hard to know the knowledge level of newcomers to the framework, so, perhaps, some of the things I’m going to mention further down won’t be new to you or won’t apply to your use case or type of project (pure as3), but they may help others in the future.

    # general rule: make sure that the event is not dispatched before you actually listen to it

    = check whether you are keeping a reference to the context

    = check whether a Mediator is able to receive events

    Creation of Mediators (from Best Practices)
    „When a view component class is mapped for mediation, you can specify if you would like to have the Mediator for the class created automatically. When this option is true the context will listen for the view component instance to dispatch its ADDED_TO_STAGE event. When this event is received, the view component will be automatically mediated and its mediator can begin to send and receive framework events“

    “It is common to add event listeners in the onRegister method of the Mediator. At this phase of the Mediator’s lifecycle, it has been registered and its view component and other dependencies have been injected.“

    If the view is already on stage before the mediator is mapped (mediatorMap.mapView) the mediator won’t be created automatically.
    (with the exception of the ContextView)

    = check whether it’s the same instance of a Mediator that is supposed to respond to an event. When Views get removed from stage, their Mediators get removed as well, when autoRemove is true (the default value of mediatorMap.mapView’s last argument).

    = check the syntax of addViewListener or eventMap.mapListener

    = make sure you’re listening for the right event

    = check whether the Event class overrides clone()

    = check whether the event bubbles, in case it’s being dispatched by subcomponents of the mediated view

    = check whether there are event names collisions

    = is there some additional logic in the onRegister() of the Mediator, besides the registration of event listeners, that could affect the way the Mediator listens for events? Service calls?

    = adding listeners to views directly can keep the mediators from being removed

    = View specific

    • components life cycle (construction, addition, initialization, invalidation, validation, updating and removal) with impact on the order of events execution or ability to listen to them
    • ItemRenderers – recycling issues
    • Resizing, Effects, Transitions, States – re-parenting issues
    • Race conditions:

    -- asynchronicity issues: network calls, streaming affecting the order of execution of other parts in your code. Network requests don't block the UI.

    -- concurrency issues: check if event listeners finish executing before the next listener is called. - data providers’ data change events, collections’ change notifications – updating and refreshing issues

    • removing and adding views in a loop can cause all Mediators (old and new) to hear the event
    • another issue with pure as3 projects could be that the Event.ADDED_TO_STAGE fires twice under certain circumstances (possible bug)
    • other FP bugs ….

    I'm sure this list is not exhaustive.

    Try to build a very simple application for learning purposes. No network calls, no videos and so on. See if a custom event from a simple child view is reaching parent view’s mediator. If it does, then you can add gradually more logic to your child view and see how it behaves. Also, investigate the way you’re adding and removing views from stage and how the framework reacts to it. Next, you can see if the VideoPlayer is causing some trouble.

    Let us know of your progress.

    Ondina

  10. 10 Posted by John R. Nyquist on 19 Sep, 2012 05:12 PM

    John R. Nyquist's Avatar

    That's a great list. I'm not new to AS3, but I am to RL, so it good to have some of these points re-enforced. I did start it with a simple example and grew it in complexity, I've been trying to mirror my structure with the structure of some examples where possible. My project is working consistently now. I am no longer bubbling the event, I have the parent re-dispatch. I've used event bubbling in AS3 for 5 years without an issue. You mentioned "it’s really better to let the parent view handle the child’s event and re-dispatch", is this an aspect of RL or do you feel it is true in general? I realize it is probably better for performance not to bubble, and it also is more explicit in your as to what you are listening/catching, those are good things :) But are there other reasons either RL-related or not that you chose to forgo that mechanism?

    Thank you for all the help and insights, Ondina!

  11. Support Staff 11 Posted by Ondina D.F. on 20 Sep, 2012 01:24 PM

    Ondina D.F.'s Avatar

    Glad you liked my list. Thank you :)

    I realize it is probably better for performance not to bubble, and it also is more explicit in your as to what you are listening/catching, those are good things :)

    Right.

    But are there other reasons either RL-related or not that you chose to forgo that mechanism?

    Nope. Robotlegs is not imposing any restrictions whatsoever on the way you’re listening for events in mediators.

    I’ll try to make my point through examples.

    Separation between framework and Views:

    Views should be unaware of the framework, and the framework should have as little knowledge about Views as even possible.
    The Views should be functional without a framework, or with any other framework, not only the current one.
    Views should be re-usable.
    Framework classes should be re-usable in case the Views change (other platform, touch and gesture user interactions….)

    Of course, Mediators, being the link between Views and Framework, have to know ‘something’ about their Views. The Mediators have access to their Views through their API, and they have to know how to translate Views events into framework events.

    In Mediator’s onRegister():

    [1] view.childView.someComponent.addEventListener(MouseEvent.CLICK, onSomeSubComponentClick, MouseEvent);

    [2] eventMap.mapListener(view.childView.someSubComponent, MouseEvent.CLICK, onSomeSubComponentClick, MouseEvent);

    [3] eventMap.mapListener(view, MouseEvent.CLICK, onSomethingClicked, MouseEvent);

    [4] eventMap.mapListener(view.someButton, MouseEvent.CLICK, onSomeButtonClicked, MouseEvent);

    [5] eventMap.mapListener(view.someButton, MouseEvent.MOUSE_OVER, onSomeButtonOver, MouseEvent);

    [6] eventMap.mapListener(view, SomeEvent.SOMETHING_HAPPENED, onSomethingHappened, SomeEvent);

    [1] and [2] The problem here is that the Mediator ‘knows too much’ about View’s subcomponents and is digging too deep into the display list’s hierarchy . Also, as mentioned in a previous post, the event listeners would keep a reference to the view, which in turn could keep the mediator from being removed. In case 1, you’d have to remove the listeners manually, if need be. The advantage of using the EventMap is that event listeners are removed automatically, when the Mediator is removed.

    [3] Here, the mediator knows only about the top level view, but the intention is not clear, and there are disadvantages like in [4] and [5]

    [4] and [5] If you changed your mind and wanted the event to be dispatched on MouseEvent. DOUBLE_CLICK instead of MouseEvent.CLICK, you’d have to change the code in your mediator accordingly. Or, if the mediator should translate click, double_click, or middle_click into the same framework event, you’d have to add events listeners for all the occurrences.

    Also, the Mediator still knows too much about the View.
    Plus, you cannot send data with plain events, unless you subclass them and add properties.

    [6] This is the better way, for a couple of reasons:

    -stronger type-safety

    -explicit intention/purpose (as you’ve noticed), clarity, easier to follow the flow. A custom event, say, FormEditEvent.FORM_EDITED, could be sent from different views to their mediators, and you’d know exactly what that means just by looking at the view’s code. This intention would be hidden in [1] to [5]

    -the Mediator is “ignorant” of which component or user interaction issued the event, the View is encapsulated and reusable. Different child views’ events can be translated into the same custom event within ParentView.

    -if you change View’s behavior, mouse over instead of click, touch and gestures instead of mouse events, replace a List with a DataGrid or vice versa, add or remove child views, Mediator’s code stays the same. You could use the same Mediator in a desktop, browser, or mobile app.

    -easier to redispatch the same event: eventMap.mapListener(view, SomeEvent.SOMETHING_HAPPENED, dispatch, SomeEvent);

    -Using a custom event makes it easier to transport data from view to the application(and of course, the other way around too, from application to mediator). The payload of the event can be anything you want it to be, but you also can use VOs as payloads.

    So, bubbling events aren’t bad per se! I actually meant scenarios like in [1] and [2], for the reasons mentioned above.
    You can let custom events bubble from deeply nested children, if you want or need to.

    Now, there is another aspect related to the topic, even if it’s not your use case:
    A ParentView with many children: ChildViewOne, ChildViewTwo…ChildViewTen.
    Decision to make:
    Should ParentViewMediator manage the events dispatched by its ParentView and also by each of its children, or would it be better to mediate each (major) ChildView?
    Answer: it depends on the use case and personal preferences. But, if you notice that there are lots and lots of subcomponents letting their events bubble up to ParentViewMediator that may be a sign that you need a more granular design for your views. Just saying :)
    [major child view = major functional area, not every and each button, list, text field etc]

    Ahh, my post ended up quite long, again ;)
    Ondina

  12. 12 Posted by John R. Nyquist on 20 Sep, 2012 05:15 PM

    John R. Nyquist's Avatar

    Well said!

  13. Support Staff 13 Posted by Ondina D.F. on 21 Sep, 2012 01:24 PM

    Ondina D.F.'s Avatar

    Thanks, John.
    I’m assuming this is resolved, thus I’m going to close this discussion. Please feel free to re-open it, if you still have any outstanding questions related to the topic.
    Cheers,
    Ondina

  14. Ondina D.F. closed this discussion on 21 Sep, 2012 01:24 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