14 Replies Latest reply on Oct 11, 2010 4:03 PM by Flex harUI

    Broadcasting events

    SiHoop Level 1

      Below are three filee: my main application which is waiting for a button to be clicked in a child component; ReturnDataEvent.as which is a custom event class; and Demo.mxml which is a component that is used to dispatch an event when the button is clicked.

       

      My problem is that I want to learn how to listen for a dispatched event anywhere in my set of files. For example, I'd like to listen for the dispatched event in some other component that does not even have a reference to Demo.

      Similarly, I'd like to listen for the event on the main file rather than on the button:

       

      this.addEventListener(Demo.BUTTON_CLICKED, buttonClicked)

       

      instead of

       

      button.addEventListener(Demo.BUTTON_CLICKED, buttonClicked)

       

      I don't really want to do this, but I'm curious as to whether it can be done? Is it possible? I suspect it is but don't know how to think about it.

      Thank you!

       

       

      <?xml version="1.0" encoding="utf-8"?>
      <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"  creationComplete="init()">
          <mx:Script>
              <![CDATA[
                  private var button:Demo;
                  private function init():void{
                      button=new Demo;
                      this.addChild(button);
                      button.addEventListener(Demo.BUTTON_CLICKED, buttonClicked)
                  }
                  private function buttonClicked(e:ReturnDataEvent):void{
                      trace("You returned the values: "+e.data1+" "+e.data2);
                  }
              ]]>
          </mx:Script>
         
      </mx:Application>

       

      Demo.mxml

       

      <?xml version="1.0" encoding="utf-8"?>
      <mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" >
          <mx:Script>
              <![CDATA[
                  import mx.events.FlexEvent;
                  public static const BUTTON_CLICKED:String="buttonClicked";
                  //Create the metadata to be used with the listener
                  [Event(name=BUTTON_CLICKED, type="events.ReturnDataEvent")]
                  protected function button1_clickHandler(event:MouseEvent):void{
                      //Instantiate the event class and access the data to be returned
                      var eventObj:ReturnDataEvent=new ReturnDataEvent(BUTTON_CLICKED);
                      eventObj.data1="aaa"
                      eventObj.data2="bbb";
                      dispatchEvent(eventObj);
                  }
              ]]>
          </mx:Script>
          <mx:Button   label="Click me!" click="button1_clickHandler(event)"/>
      </mx:Canvas>

       

      ReturnDataEvent.as

       

      package {
          import flash.events.Event;
         
          public class ReturnDataEvent extends Event{
              public var data1:String;
              public var data2:String;
             
              public function ReturnDataEvent(type:String){
                  super(type);
              }
              override public function clone():Event{
                  var eventObj:ReturnDataEvent= new ReturnDataEvent(type);
                  eventObj.data1=data1;
                  eventObj.data2=data2;
                  return eventObj;
              }
          }
      }

       

      Message was edited by: SiHoop

        • 1. Re: Broadcasting events
          Flex harUI Adobe Employee

          You can bubble events and lots of folks do that, but I don't advise it.  It

          isn't very OO, IMHO.

           

          My preference is to consider the semantics of the event. Usually the

          container has its own semantics for the event.  For example, a click on an

          item renderer should generate an ITEM_CLICK event, or a click on a scrollbar

          button should generate a SCROLL event, and the listener should generally be

          more interested in the semantic event than the low-level event.

          • 2. Re: Broadcasting events
            SiHoop Level 1

            Would you mind showing what a listener would look like to capture the event? It's obvious what to do if I'm just sending an event back to the place that instantiated the component. But how can I capture the event further down the line (if that makes sense).

             

            Also, how do people do it with bubbling?

            Thanks for your last reply!

            • 3. Re: Broadcasting events
              Flex harUI Adobe Employee

              Set the bubbles parameter to true on the event constructor

              1 person found this helpful
              • 4. Re: Broadcasting events
                SiHoop Level 1

                Thanks that helps and I'll try it. However, by your own words, bubbling is not a strong solution. You favor the notion of the 'semantics of the event'. That is a somewhat cryptic allusion to the better solution. Could I ask you to 'unpack' its meaning?

                • 5. Re: Broadcasting events
                  Flex harUI Adobe Employee

                  I attempted to do so in the first reply.  A click event at the low level

                  might become a scroll event at the next level, which might become a

                  viewportChange event at the level above that.  And the clients only listen

                  to the top-most level and generally don't care the low-level events.

                  • 6. Re: Broadcasting events
                    SiHoop Level 1

                    I tried your solutions. Using bubbles worked perfectly.

                    However, I still don't understand how your second solution relates to me so I've put together another case in the hope that my issue will become clearer. My main file creates a component: Canvas.mxml. Canvas.mxml creates a component Button.mxml. When Button is clicked it dispatches an event: ReturnDataEvent.as which is a custom event class.

                     

                    My goal is to create a listener in my main file that is capable of responding to the button click. I want the click to pass up through the sequence of components, but I'd like to do it the 'right' way instead of using bubbles. I assume that my event listener in the main file is completely wrong, but I don't know how to change it.

                    My apologies for taking so much of your time, but thank you!

                     

                    Here's my code:

                    Main file

                    <?xml version="1.0" encoding="utf-8"?>
                    <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"  creationComplete="init()">
                        <mx:Script>
                            <![CDATA[
                                private var canvas:Canvas;
                                private function init():void{
                                    canvas=new Canvas;
                                    this.addChild(canvas);
                                    canvas.addEventListener(Button.BUTTON_CLICKED, buttonClicked)
                                }
                                private function buttonClicked(e:ReturnDataEvent):void{
                                    trace("You returned the values: "+e.data1+" "+e.data2);
                                }
                            ]]>
                        </mx:Script>
                    </mx:Application>

                     

                    Canvas.mxml

                    <?xml version="1.0" encoding="utf-8"?>
                    <mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:local="*" >
                        <mx:Script>
                            <![CDATA[

                     

                            ]]>
                        </mx:Script>
                        <local:Button />
                    </mx:Canvas>

                     

                    Button.mxml

                    <?xml version="1.0" encoding="utf-8"?>
                    <mx:Button xmlns:mx="http://www.adobe.com/2006/mxml" label="Click me!" click="button1_clickHandler(event)">
                        <mx:Script>
                            <![CDATA[
                                import mx.events.FlexEvent;
                                public static const BUTTON_CLICKED:String="buttonClicked";
                                 [Event(name=BUTTON_CLICKED, type="events.ReturnDataEvent")]
                                protected function button1_clickHandler(event:MouseEvent):void{
                                    //Instantiate the event class and access the data to be returned
                                    var eventObj:ReturnDataEvent=new ReturnDataEvent(BUTTON_CLICKED);
                                    eventObj.data1="aaa"
                                    eventObj.data2="bbb";
                                    dispatchEvent(eventObj);
                                }
                            ]]>
                        </mx:Script>
                    </mx:Button>

                     

                    ReturnDataEvent.as

                    package {
                        import flash.events.Event;
                       
                        public class ReturnDataEvent extends Event{
                            public var data1:String;
                            public var data2:String;
                           
                            public function ReturnDataEvent(type:String){
                                super(type);
                            }
                            override public function clone():Event{
                                var eventObj:ReturnDataEvent= new ReturnDataEvent(type);
                                eventObj.data1=data1;
                                eventObj.data2=data2;
                                return eventObj;
                            }
                        }
                    }

                    • 7. Re: Broadcasting events
                      Flex harUI Adobe Employee

                      From an OO perspective, how would you know your Canvas has a Button that

                      would dispatch such an event?  The class names are to generic to indicate

                      other semantics.

                       

                      Suppose instead that your Canvas was a DataEntryForm.  It could contain many

                      buttons only one of which indicated that the data was ready for other

                      processing.

                       

                      In such a case, I would not create a custom button.  Instead, the

                      DataEntryForm would catch the click from the one button, and it would

                      dispatch the custom event, and you'd listen to the DataEntryForm without any

                      need for bubbling.

                      • 8. Re: Broadcasting events
                        SiHoop Level 1

                        I understand what you are saying, but the answer to your approach is what I cannot figure out. How to I listen for the dispatched event back in my main file? The listener I have at the moment is tied to the Button click and only works if bubbles is true. What I want is to listen for is an event in the Button (or DataEntryForm) where bubbles is false but I don't know how to do that on my main file.I assume that the event should not be Button.BUTTON_CLICKED, but that is the event that I have created for one specific action.

                        What is the event that I should be listening for?

                        • 9. Re: Broadcasting events
                          Flex harUI Adobe Employee

                          The point is that, from an OO perspective, you should not know that

                          DataEntryForm contains a Button that tells you when the data is ready.  The

                          DataEntryForm itself should dispatch a custom event w/o bubbling, and your

                          app would listen to the DataEntryForm for that event.  Logic in

                          DataEntryForm would decide to dispatch that custom event based on its

                          internal implementation, which for this version, might happen to be the

                          Button you are currently using.

                           

                          The point of OO abstraction is that the app should not need to know if the

                          guts of the DataEntryForm changes someday.  There is a semantic event that

                          tells you the data is ready, but it doesn't have to be tied to any sort of

                          Button event.  In fact, a keyboard shortcut should/could also signify that

                          the data is ready, and someday, your mobile version might just use some sort

                          of multi-touch gesture, or speech recognition and not a button at all.

                           

                          You might therefore go with a DataReady or FormComplete event instead.

                          • 10. Re: Broadcasting events
                            SiHoop Level 1

                            Sorry, but I still don't get it! I found some code on the Adode site that I thought would explain it for me, but I think I've ended up creating exactly what I had before. I have a main file which create s a DataEntryForm. The DataEntryForm contains a series of Buttons. The idea is that when a Button is clicked, a message will be sent to the main file telling which button has been clicked. It works if bubbles=true, else it fails. I just cannot figure out how to connect an event listener to the dispatched event.

                             

                            <?xml version="1.0"?>
                            <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:local="*"  creationComplete="init()">
                                <mx:Script>
                                    <![CDATA[
                                        public function init():void{
                                            this.addEventListener("enableChanged", buttonClicked)
                                        }
                                        private function buttonClicked(e:EnableChangeEvent):void{
                                            trace("You selected the button: "+e.target);
                                        }
                                    ]]>
                                </mx:Script>
                                <local:DataEntryForm />   
                            </mx:Application>

                             

                            DataEntryForm.mxml

                            <?xml version="1.0"?>
                            <mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:local="*" >
                                <local:MyButton  id="button0"/>   
                                <local:MyButton  id="button1"/>   
                                <local:MyButton  id="button2"/>   
                                <local:MyButton  id="button3"/>   
                            </mx:HBox>

                             

                            MyButton.mxml

                            <?xml version="1.0"?>
                            <mx:Button xmlns:mx="http://www.adobe.com/2006/mxml" click="dispatchEvent(new EnableChangeEvent('enableChanged'));">   
                                <mx:Metadata>
                                    [Event(name="enableChanged", type="EnableChangeEvent")]
                                </mx:Metadata>
                            </mx:Button>

                             

                            EnableChangeEvent .as

                            package {
                                import flash.events.Event;
                                public class EnableChangeEvent extends Event{
                                    public function EnableChangeEvent(type:String, bubbles:Boolean=true) {
                                        super(type, bubbles);
                                    }
                                }
                            }

                             

                            Message was edited by: SiHoop

                            • 11. Re: Broadcasting events
                              Flex harUI Adobe Employee

                              The point of OO abstraction is that the folks on the outside don't know

                              anything about the internals.  Suppose you decide to remove the buttons and

                              replace with voice recognition events (if there was such a thing)?

                               

                              I would write it as follows:  The advantage will be that you might want

                              other logic to determine whether or not to dispatch that event and that

                              logic should not be in the buttons. They are just buttons and have no

                              knowledge of how they are being used.  And if I add a keyDown listener for a

                              keyboard shortcut, I can also just call that event.  And if I add some voice

                              recognition logic someday, I would again only have to call that one method.

                               

                              DataEntryForm.mxml

                              <?xml version="1.0"?>

                              <mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:local="*" >

                                  <mx:Metadata>

                                      [Event(name="enableChanged", type="EnableChangeEvent")]

                                  </mx:Metadata>

                                  <mx:Script>

                                      private function determineWhetherToDispatchEnableChangeEvent():void

                                      {

                                          if (...)

                                              dispatchEvent(new EnableChangeEvent('enableChanged'));

                                      }

                                  </mx:Script>

                                  <mx:Button  id="button0"

                              click="determineWhetherToDispatchEnableChangedEvent()" />   

                                  <mx:Button  id="button1"

                              click="determineWhetherToDispatchEnableChangedEvent()" />   

                                  <mx:Button  id="button2"

                              click="determineWhetherToDispatchEnableChangedEvent()" />   

                                  <mx:Button  id="button3"

                              click="determineWhetherToDispatchEnableChangedEvent()" />   

                              </mx:HBox>

                               

                              • 12. Re: Broadcasting events
                                Claudiu Ursica Level 4

                                DataEntryForm.mxml registers/adds listeners for mouse clicks from buttons and

                                dispatches the change event to main app attaching whatever payload is needed

                                need into the custom event.

                                 

                                 

                                C

                                • 13. Re: Broadcasting events
                                  SiHoop Level 1

                                  OK-- now I'm getting it. Rather than in inside the component, I have to register my listener on the parent object and dispatch an event from the parent when the button has been triggered.

                                  One final question: Suppose I have a series of 10 components that are embedded within each other and the last one is a button. When I click on the button I want to send an event from the innermost component back up to the main app. I know I can do that with a custom event where I set bubbles=true. However, is there a way to quickly send the message from component 10 to the main app without having a listener on each successive parent component, which in turn dispatches another event?

                                   

                                  I really appreciate all the help you've given me over the past few days-- thank you!

                                  • 14. Re: Broadcasting events
                                    Flex harUI Adobe Employee

                                    You can dispatch an event from just about anywhere, as long as that this is

                                    an IEventDispatcher, and some other code is listening to that thing, but the

                                    key point is still that from an OO perspective, that is breaking

                                    encapsulation and may not be what you want in the long run.

                                     

                                    There are plenty of exceptions, like when randomly placed things like

                                    radiobuttons register with a radiobuttongroup and dispatch via the group.