18 Replies Latest reply on Sep 3, 2009 9:30 AM by Flex harUI

    Can't detect events in class extending UIComponent?

    PeakDigital Level 1

      I am working on breaking into event-based communication between classes. I am referencing Greg Lafrance's Wizard MVC sample. Most of the project is going well, but detecting events has me stumped.

       

      The ListMgrLocationsController() function appears to run normally when I watch it in debug mode, but the setupEventListeners() function never runs after it is assigned as a listener.

       

      It appears that the creationComplete event of the child's controller class is never sent.  I have tried listening for the Event.ADDED and Event.ADDED_TO_STAGE events (by creating simple listener functions with trace commands), and neither of them are detected either.

       

      Here is the code from that class:

       

      public class ListMgrLocationsController extends UIComponent
              {
              private static var instance:ListMgrLocationsController;
              
              public function ListMgrLocationsController()
                  {
                  super();
                  this.addEventListener(FlexEvent.CREATION_COMPLETE, setupEventListeners);
                  
                  // this runs, but generates 1009 error when it runs.: setupEventListeners();
                  
                  // The constructor enables us to ensure only one instance of the
                  // model exists at any time (singleton).
                  if( instance != null )
                      {
                      trace("illegal request for new ListMgrLocationsController");    
                      throw( new Error( "There can only be one instance of ListMgrLocationsController." ) );
                        }
                  }
              
              private function setupEventListeners():void
                  {
                  systemManager.addEventListener(ListManagerTreeItemClickEvent.LISTMGR_TREE_ITEM_CLICKED,getLocationChildrenFromEvent);
                  trace("sysman listener for LMTICEvent added in ListMgrLocationsController");
                  }
              
              
              public function getLocationChildrenFromEvent(event:ListManagerTreeItemClickEvent):void
                  {//gathers a list of locations assigned to the ParentID provided.
                  trace("event listener getLocationChildrenFromEvent fired");
                  var intLocationParentID:int=event.eventData[0].treeClickedItemID;
                  
                  //do some other things.
                  }
              
              //this function collected from Greg Lafrance's Wizard MVC example app.
               public static function getInstance():ListMgrLocationsController
                   {
                  if( instance == null )
                      {
                      instance = new ListMgrLocationsController();
                        }
                        return instance;
                  }
              }
          }

      Any ideas to get me past this roadblock?

       

      Thanks.

      Paul

        • 1. Re: Can't detect events in class extending UIComponent?
          babo_ya Level 3

          Two things to try..

           

          add the

           

           super();
                      this.addEventListener(FlexEvent.CREATION_COMPLETE, setupEventListeners);

          on the createChildren(); as below

           

          protected override function createChildren():void

          {

          super();

          this.addEventListener(FlexEvent.CREATION_COMPLETE, setupEventListeners);

           

           

          }

           

           

          BaBo,

          • 2. Re: Can't detect events in class extending UIComponent?
            Flex harUI Adobe Employee

            Any function assigned in addEventListener must accept Event or a subclass as its first (and usually only) parameter

             

            Function setupEventListeners(event:Event):void

             

             

            Alex Harui

            Flex SDK Developer

            Adobe Systems Inc.

            Blog: http://blogs.adobe.com/aharui

            • 3. Re: Can't detect events in class extending UIComponent?
              PeakDigital Level 1

              Thank you for your suggestions.

               

              I tried both suggestions, with no improvement.

               

              Any other ideas?

               

              If I try to call my setupListeners() directly from another class with this syntax:  ListMgrLocationsController.getInstance().setupEventListeners()  then the function does run, but results in a 1009 as if the ListMgrLocationsController class still hasn't been created.

               

              Very confused.

              • 4. Re: Can't detect events in class extending UIComponent?
                danwize
                Instead of CREATION_COMPLETE, try APPLICATION_COMPLETE.  In my experience many items are not yet 
                initialized when CREATION_COMPLETE fires.

                Dan
                • 5. Re: Can't detect events in class extending UIComponent?
                  PeakDigital Level 1

                  Dan,  Thank you for the input. Unfortunately APPLICATION_COMPLETE produces the same effect: nothing.

                   

                  ================

                   

                  Here is what I have learned since my last post:

                   

                  1. The 1009 error occurs any time I try to use "systemManager". I changed the listener assignment in my child class to this.addEventListener(ListManagerTreeItemClickEvent.LISTMGR_TREE_ITEM_CLICKED,getLocationC hildrenFromEvent); instead of systemManager.addEventListener(... and the 1009 error no longer occurs.   I tried placing the systemManager call into the parent class, and it too resulted in a 1009.   The parent class has "import mx.managers.SystemManager;" in the imports section at the top.

                   

                  2. I can successfully set up a listener in the child class (well, the code runs without errors anyway) by using the syntax

                  ListMgrLocationsController.getInstance().setupEventListeners(); in the parent class instead of internally listening for the CREATION_COMPLETE that never seems to happen.

                   

                  3. With the listener for the custom event set locally in the child class and the child class's setupEventListeners called directly from the parent class, as shown in #1,#2 above, no errors occur. Trace commands show the event to be dispatched as expected. However, the listener in the child class never hears the event.

                   

                  4. The custom event is set for bubbles=true, since my understanding is that this is the only way for an event dispatched in component A to be "heard" by component B. If I'm wrong and there is another method for cross-component communication, I'd appreciate learning it. Of course, even with bubbles=true the child class isn't hearing the event.

                   

                  5. The classes I am working with for this problem are all contained in a popup TitleWindow. The main app has other functions that could be used for unlimited time prior to this TitleWindow being called up by the user.

                   

                  ---

                  I have lost 2 days to this problem now. What is even more frustrating is that it was working fine with an "improper" method... All I have to do is call

                  ListMgrLocationsController.getInstance().getLocationChildren(event.currentTarget.selectedI tem.@ID); and the data I need to work with in the child class is transferred to it and the proper function executes. However, I am determined to build this project as loosely coupled as possible and utilize custom events, since all the reading I have done indicates that is the proper and preferred method.

                   

                  Here is the full code of my custom event. Maybe I have something wrong in here?

                   

                  package com.myproject.classes.events
                  {
                      import flash.events.Event;
                  
                      public class ListManagerTreeItemClickEvent extends Event
                      {
                          public static const LISTMGR_TREE_ITEM_CLICKED:String = "LISTMGR_TREE_ITEM_CLICKED";
                          public var eventData:Object;
                          public function ListManagerTreeItemClickEvent(type:String, eventData:Object, bubbles:Boolean=true, cancelable:Boolean=false)
                              {//the eventData property carries whatever data needs to be transmitted by the event.
                              super(type, bubbles, cancelable);
                              this.eventData=eventData;
                              }
                      }
                  }
                  

                   

                   

                  Thanks to all for any help you can provide.

                  Paul

                  • 6. Re: Can't detect events in class extending UIComponent?
                    danwize Level 1

                    Here's a custom event class I wrote that woks.  Note the two override functions at the end.  For a custom event to work you must override clone, and it is recommended to override toString.

                     

                    package proj.fuego.listenEngine.events

                    {

                    import flash.display.Sprite;

                    import flash.events.Event;

                     

                    /**

                    * This class allows an event to be fired that can pass a vector containing all the puzzle pieces that need to be manipulated to anything that is listening for PuzzleEvents.

                    * @author Daniel B. Davis

                    *

                    */

                    public class PuzzleEvent extends Event

                    {

                    public static var TWEEN_PIECE:String = "TWEEN_PIECE";

                    public var pieces:Vector.<Sprite>;

                     

                    /**

                    * Creates a new Puzzle Event

                    * @param type

                    * @param pieces A sprite vector containing all the puzzle pieces.

                    * @param bubbles

                    * @param cancelable

                    *

                    */

                    public function PuzzleEvent(type:String, pieces:Vector.<Sprite>, bubbles:Boolean=false, cancelable:Boolean=false)

                    {

                    super(type, bubbles, cancelable);

                    this.pieces = pieces;

                    }

                    /**

                    *

                    * @return Returns a new Puzzle Event

                    *

                    */

                    override public function clone():Event

                    {

                                return new PuzzleEvent(type, pieces, bubbles, cancelable);

                            }

                     

                            /**

                             *

                             * @return Returns the event in string fomat.

                             *

                             */

                            override public function toString():String

                            {

                                return formatToString("PuzzleEvent", "type", "pieces", "bubbles", "cancelable", "eventPhase");

                            }

                    }

                    }

                     

                     

                    Go here for more info.  He does a great job of explaining how to do this.  That's where I learned it from

                     

                    http://3lbmonkeybrain.blogspot.com/2008/01/how-to-extend-event-once-and-for-all.html

                    • 7. Re: Can't detect events in class extending UIComponent?
                      PeakDigital Level 1

                      Dan, Thanks for following up with the sample and link.  I modified my event per your info, but I'm still not getting any results.

                      =========

                      Current custom event class code:

                      package com.myproject.classes.events
                      {
                          import flash.events.Event;
                      
                          public class ListManagerTreeItemClickEvent extends Event
                          {
                              public static const LISTMGR_TREE_ITEM_CLICKED:String = "LISTMGR_TREE_ITEM_CLICKED";
                              public var eventData:Object;
                              public function ListManagerTreeItemClickEvent(type:String, eventData:Object, bubbles:Boolean=true, cancelable:Boolean=false)
                                  {//the eventData property carries whatever data needs to be transmitted by the event.
                                  super(type, bubbles, cancelable);
                                  this.eventData=eventData;
                                  }
                      
                          override public function clone():Event
                              {
                              return new ListManagerTreeItemClickEvent(type, eventData, bubbles, cancelable);
                              }
                           
                           override public function toString():String
                              {
                              return formatToString("ListManagerTreeItemClickEvent", "type", "eventData", "bubbles", "cancelable", "eventPhase");
                              }
                          }
                      }
                      

                       

                      The function that sets up the listener in the child component class:

                              public function setupEventListeners():void
                                  {
                                  this.addEventListener(ListManagerTreeItemClickEvent.LISTMGR_TREE_ITEM_CLICKED,getLocationChildrenFromEvent);
                                  trace("local listener for LMTICEvent added in ListMgrLocationsController");
                                  }
                      

                      I have tried both this.addEventListener and systemManager.addEventListener. The systemManager request still results in a 1009 error.

                       

                      The function that dispatches the event. This resides in my parent component class:

                       

                      private function captureSelectedItemThenDispatchEvent(event:Event):void
                                  {//detect the label and ID of the selected item in the tree. this replaces captureSelectedItem
                                  var objTreeItemData:Object=new Object;
                                  objTreeItemData.treeClickedItemID=event.currentTarget.selectedItem.@ID;
                                  dispatchEvent(new ListManagerTreeItemClickEvent(ListManagerTreeItemClickEvent.LISTMGR_TREE_ITEM_CLICKED, objTreeItemData));
                                  trace ("custom event dispatched in captureSelectedItemThenDispatchEvent()");
                                  }
                      

                       

                      I think I must be either missing an essential bit of code, or have written something that is blocking my event somehow.  It seems odd that every time I attempt to utilize systemManager I get a 1009 error. Not sure if there is a connection between these problems?

                      • 8. Re: Can't detect events in class extending UIComponent?
                        danwize Level 1

                        Paul,

                         

                        What is your child class (are you extending some flex component?  How are you loading it?

                         

                        Make sure the event listener is added before the expected event dispatch.  I only say that because I had the same issue.  By the time I added the event, the function that dispatched the event had already fired.

                         

                        Dan

                        • 9. Re: Can't detect events in class extending UIComponent?
                          PeakDigital Level 1

                          Thanks for sticking with me on this.  The event listener is added during initialization, the event isn't dispatched until a mouse click is detected.

                           

                          The child class extends UIComponent and is a singleton. It is loaded by another class that also extends UIComponent, loading occurs via a getInstance() function.

                           

                              public static function getInstance():ListMgrLocationsController
                                       {
                                       trace("ListMgrLocationsController.getInstance fired");
                                      if( instance == null )
                                          {
                                          instance = new ListMgrLocationsController();
                                          }
                                            return instance;
                                      }

                           

                          Since my last post, I have found that the child class can detect and handle the event if I dispatch it like this:

                           

                          ListMgrLocationsController.getInstance().dispatchEvent(new ListManagerTreeItemClickEvent(ListManagerTreeItemClickEvent.LISTMGR_TREE_ITEM_CLICKED, objTreeItemData));

                          - essentially reaching into the child class and dispatching the event within it. That hardly seems like the best way to approach the problem, but oddly enough it works. Unfortunately, doing it that way, I can't detect it in the parent class. I need to be able to listen for this event in both the parent and child, as it will affect what is done in both classes.

                           

                          I also tried adding an event listener in the main application file, and even that can't detect the event firing. I have tried it with bubbles= both false and true, no change in behavior. It is as if the event is trapped within the component and nothing above or below it can detect it.

                          • 10. Re: Can't detect events in class extending UIComponent?
                            Flex harUI Adobe Employee

                            Singletons should not be based on UIComponent.  I would use EventDispatcher instead.  UIComponents generally are not singletons and get added to the display list and display things.  When added, their systemManager property becomes valid.

                             

                            Looks like you are writing a controller singleton.  The singleton typically calls dispatchEvent on itself and does not bubble events.  Bubbling has no effect on non-displayobjects.  When created, a controller may use Application.application to attach listeners to UI objects.  Code that wants to listen to the controller singleton use ListMgrLocationsController.getInstance().addEventListener.

                             

                            Alex Harui

                            Flex SDK Developer

                            Adobe Systems Inc.

                            Blog: http://blogs.adobe.com/aharui

                            1 person found this helpful
                            • 11. Re: Can't detect events in class extending UIComponent?
                              danwize Level 1

                              This is kind of an ugly possible soloution, but try just listening for the click event in the child component and then dispatch a new event.  Listen for that new event in the parent.  Though it is strange that even setting bubbles to true won't work.

                              1 person found this helpful
                              • 12. Re: Can't detect events in class extending UIComponent?
                                danwize Level 1

                                It looks like you might already be doing that.  Dispatching the event I mean.  If so never mind.

                                • 13. Re: Can't detect events in class extending UIComponent?
                                  PeakDigital Level 1

                                  Thank you for the additional input.

                                   

                                  Dan, yes I think I am already trying that.

                                   

                                  Alex,  There's no end of new classes in Flex is there?    Thanks for pointing that out, and why it should be used. I have converted both classes to extend EventDispatcher.

                                   

                                  However, I am still running into a problem with systemManager.  Now, in Flex Builder it is displaying error 1120:Access of undefined property systemManager on the line where I try to define a listener:

                                     public function setupEventListeners():void
                                              {
                                              systemManager.addEventListener(ListManagerTreeItemClickEvent.LISTMGR_TREE_ITEM_CLICKED,ge tLocationChildrenFromEvent);
                                              trace("setupEventListeners() called in ListMgrLocationsController");
                                              }

                                   

                                  I have already tried rebuilding the class from the start. It didn't show that error until I tried to replace my original class with the rebuilt one. Odd, maybe some corruption in the project manager files for Flex Builder??

                                   

                                  I guess I'll try rebuilding it over again tomorrow morning, and maybe save it as a different filename.

                                   

                                  Thanks.

                                  Paul

                                   

                                  ===========

                                   

                                  edit... I just re-read Alex's post again, maybe I misunderstood the usage. Will look again in the morning...

                                  • 14. Re: Can't detect events in class extending UIComponent?
                                    Flex harUI Adobe Employee

                                    I'm not sure which class has setupEventListeners.  If it is your controller, it should probably listen to Application.application.systemManager.  Now that you've converted it to EventDispatcher there is no systemManager on that class, and when it was a UIComponent, it wasn't on the display list so systemManager was null.

                                     

                                    Alex Harui

                                    Flex SDK Developer

                                    Adobe Systems Inc.

                                    Blog: http://blogs.adobe.com/aharui

                                    • 15. Re: Can't detect events in class extending UIComponent?
                                      PeakDigital Level 1

                                      I shouldn't have been working on this when I was so tired last night. Misreading information doesn't help debugging - didn't realize there was no systemManager on EventDispatcher, even though I had the info in front of me in the documentation.

                                       

                                      I think I have probably painted myself into a corner with the way I have my classes structured. Placing these items and the .mxml components they control into a popup probably complicates things further, if my understanding is correct that a TitleWindow managed by PopUpManager has a different displaylist than the main app.

                                       

                                      Thank you for the further education. I'll keep working on this...

                                      • 16. Re: Can't detect events in class extending UIComponent?
                                        Flex harUI Adobe Employee

                                        A controller shouldn't be a UIComponent.  It just needs to listen to interaction events from the UI and update the model.  To get events from anywhere in the display list, systemManager is the right place to listen (actually, getSandboxRoot()).  Non-UIComponents need a context to find the systemManager.  Application.application is a reasonable place.  You can also implement IMXMLObject and get the systemManager of the document.

                                         

                                        Alex Harui

                                        Flex SDK Developer

                                        Adobe Systems Inc.

                                        Blog: http://blogs.adobe.com/aharui

                                        • 17. Re: Can't detect events in class extending UIComponent?
                                          PeakDigital Level 1

                                          As suggested the controllers that extended UIComponent have been converted to extend EventDispatcher.

                                           

                                          Here is my most recent attempt, still no success. This function is in the controller class for the child .mxml component.

                                           

                                                  public function setupEventListeners():void
                                                      {
                                                      Application.application.systemManager.getSandboxRoot().addEventListener(ListManagerTreeItemClickEvent.LISTMGR_TREE_ITEM_CLICKED,testAppappsysmanListen);
                                                      trace("setupEventListeners() called in ListMgrLocationsController");
                                                      }
                                                  
                                                  public function testAppappsysmanListen(event:ListManagerTreeItemClickEvent):void
                                                      {
                                                      trace("testAppappsysmanListen fired in ListMgrLocationsController.as");
                                                      }

                                           

                                          When I set up a listener outside of the current component, do I have to address the listener function somehow so that the systemManager knows where to find it, or is that handled automatically?

                                           

                                          Edit: Here is the code that dispatches the event. It is in the controller class for the parent .mxml component. The captureSelectedItemThenDispatchEvent() is the handler for the item click event in my Tree control:

                                                  private function captureSelectedItemThenDispatchEvent(event:Event):void
                                                      {//detect the label and ID of the selected item in the tree. this replaces captureSelectedItem
                                                      
                                                      _stringAncestryBreadcrumb="";//reset the breadcrumb string
                                                      _arrcollAncestors.removeAll(); //reset the breadcrumb source array
                                                      
                                                      traceXMLAncestors(event.currentTarget.dataProvider,event.currentTarget.selectedItem);
                                          
                                                     var objTreeItemData:Object=new Object;
                                                      objTreeItemData.treeClickedItemID=event.currentTarget.selectedItem.@ID;
                                                      
                                                      //next line for testing - to see if app can detect the event if it is not fired from within the child class.
                                                      //does not work:dispatchEvent(new ListManagerTreeItemClickEvent(ListManagerTreeItemClickEvent.LISTMGR_TREE_ITEM_CLICKED, objTreeItemData));
                                          
                                                      //dispatch an event at the current level (Main list manager shell)
                                                      this.dispatchEvent(new ListManagerTreeItemClickEvent(ListManagerTreeItemClickEvent.LISTMGR_TREE_ITEM_CLICKED,objTreeItemData));
                                                      
                                                      Application.application.dispatchEvent(new ListManagerTreeItemClickEvent(ListManagerTreeItemClickEvent.LISTMGR_TREE_ITEM_CLICKED,objTreeItemData));
                                                                  
                                                      trace ("custom event dispatched in captureSelectedItemThenDispatchEvent()");
                                                      }
                                          

                                           

                                          Thanks for your help and patience.

                                           

                                          Paul

                                          • 18. Re: Can't detect events in class extending UIComponent?
                                            Flex harUI Adobe Employee

                                            Let's assume that LISTMGR_TREE_ITEM_CLICKED is dispatched by a Tree in a view somewhere on the display list.  There are several designs for capturing events from randomly placed UI widgets.  The one I recommend is to have the UI widgets in the view dispatch useful events on the controller itself or call a method on the controller.  UI widgets generally dispatch lots of events as they get mouse and keyboard events and change state in response to those events, but only a few of those events have meaning to the controller and the view designer knows which ones those are and can either dispatch an event from the controller or call a method on its controller.

                                             

                                            Other folks bubble events which I do not recommend, because then you have to have unique event names throughout your application.  But then listening to systemManager will catch the events, so that's the fastest way to get your code running.

                                             

                                            Other folks capture events which I also do not recommend.  Again, event names have to be unique.  To me, bubbling is announcing to everyone what you're doing, and capturing is listening to everyone.  Better to have a known contract for a direct conversation between the view and controller.

                                             

                                            Alex Harui

                                            Flex SDK Developer

                                            Adobe Systems Inc.

                                            Blog: http://blogs.adobe.com/aharui