Thursday, February 9, 2012

Flex Events (Part One)

This series is a basic to advanced introduction to Flex Events. I have explained that in terms of question and answers so that it is equally useful for interview purpose as well. All comments are welcomed!!


Question: What is an event?
Answer: An event is an object of flash.events.Event class. This is an implicitly created object, similar to request and response objects in a JSP, which are implicitly created by application server. Many classes extend that class and are defined in mostly packages: spark.events.*, mx.events.* and flash.events.*. The Spark package defines event classes that are specific to a few spark controls, including the TextOperationEvent and VideoEvent. The MX package defines events that are specific to Flex controls e.g. DataGridEvent, DragEvent and ColorPickerEvent. The last package describes events that are not unique to Flex, but are instead defined by Flash Player e.g. MouseEvent, DataEvent and TextEvent.

Question: What are the various phases in event propagation?
Answer: When Flash player dispatches an Event object, that Event Object makes a roundtrip journey from the root of the display list to the target node, checking each node for registered listeners. The target node is the node where event occurred. The eventPhase property takes three values: EventPhase.CAPTURING_PHASE, EventPhase.AT_TARGET and EventPhase.BUBBLING_PHASE so the event flow is divided into three phases: capturing phase, targeting phase and bubbling phase.

Capturing Phase: In this phase, flash player examines each node from root to the parent of target node to see if it has a listener registered to handle the event. If it does, FP sets the appropriate value of Event object and then calls that listener.

Targeting Phase: This phase consists solely of the target node.  FP sets the proper values of Event object, cheks the target node for registered event listeners and then calls those listeners.

Bubbling Phase: This phase considers all the nodes from parent of target node to the root node. FP sets proper value on Event object and then calls event listeners on each of these nodes. Flash Player stops after calling any listeners on the root node.

During each phase the nodes have a chance to react to the event. For example consider a button inside a vbox in an application.
                                                          

If a user clicks on this button, Flex checks the application and the Vbox for listeners during capturing phase. Flex then triggers the Button’s listeners in target phase. In the bubbling phase, the VBox and then the application are again given a chance to handle the event, but now in reverse order of capturing phase. Not all events participate in all the phases. Some events are directly dispatched to target node and don’t participate in bubbling and capturing phase.

In action script we can register listener on a target node and on any other node along the flow. Some events may target objects that are not on display list, such as events dispatched to an instance of Socket class. These event objects flow directly to the target node, without participating in capturing or bubbling phase. We can also cancel an event as it flows through the event model even though it was supposed to continue to other phases. We can do this if cancelable property is true.




Any event can be captured but no display object listens during capturing phase unless we explicitly instruct them to do so. Capturing is disabled by default. The only way to add a listener during this phase is to pass true for the use_capture when calling the addEventListener().If we add an event listener inline with MXML, Flex sets this argument to false; you cannot override it.
myPanel.addEventListener(MouseEvent.MOUSE_DOWN, clickHandler, true);

If we set the use_capture argument to true the event can still bubble, but capture phase listeners will not react to it. If you want your event to traverse both the capturing and bubbling phases, you must call addEventListener() twice: once with use_capture set to true, and then again with use_capture set to false. The capturing phase is very rarely used, and it can also be computationally intensive. By contrast, bubbling is much more common.
An event can bubble if its bubble property is set to true. The Object that the event is currently bubbling thorough is the currentTarget.

Question: What are target and currentTarget properties?
Answer:  For an Event object, target refers to the dispatcher of this event and currentTarget refers to the current node that is being examined. When we handle a mouse event (say click) on a button then the event.target may be the subcomponent UITextField that defines the label, is user click on the label. So Button is the object listening to the event, but it’s the UITextField that dispatched the event. During the targeting phase the value of both are same.




<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="createLinkHandler();">

    <mx:Script><![CDATA[

        private function linkHandler(event:MouseEvent):void {

        var b:Boolean = event.target.hasOwnProperty("label"); 

        if (b) {

           var url:URLRequest = new URLRequest( 

            "http://finance.google.com/finance?q="+event.target.label);

           navigateToURL(url);

         }   

        }

        private function createLinkHandler():void {

            p1.addEventListener(MouseEvent.CLICK,linkHandler);

        }

    ]]></mx:Script>

    <mx:Panel id="p1" title="Click on a stock ticker symbol">

        <mx:LinkButton label="ADBE"/>

        <mx:LinkButton label="GE"/>

        <mx:LinkButton label="IBM"/>

        <mx:LinkButton label="INTC"/>

    </mx:Panel>

</mx:Application>





Here, all children of panel inherit the same listener. As Flex invokes the handler on bubbled event, we use target rather than the currentTarget property.

Question: Which function is responsible for setting the target property of an event?
Answer: dispatchEvent()

Question: What are the bubble and cancellable property of an event?
Answer: Both are Boolean properties. The first property specified whether event can bubble or not and later specifies whether event can be cancelled or not. If it can be cancelled then we can use preventDefault() method.

Question: What is an event listener/handler?
Answer:  An event listener is the function that handles the event. This needs to be registered to display list using addEventListener() method in action script, or it can also be called in mxml. For example:
But it is best practice to use addEventListener method, because:
  1. This method gives more control over event by letting us configure priority, capture settings and use event constants.
  2. Also if an event is added using this method, it can be removed using removeEventListener() method.
If we need to work with the Event object it need to be passed in mxml tag using Event keyword, but it is not necessary to pass it in mxml. But in action script if we don’t specify an argument in event handler, event is passed anyway and we will get a runtime error. An event listener can be registered with an object only if it dispatches that kind of event. For example a Form containing a button cannot dispatch an event of type click, as click events are not dispatched by a Form.




Question: How to dispatch an event in Flex?
Answer: An Event object needs to be created before dispatching it.

Event(event_type:String, bubbles:Boolean, cancelable:Boolean)


The method dispatchEvent() is used to dispatch the event. This method is inherited from EventDispatcher class, which is extended by UIComponent class. So all components which extend UIComponent class can dispatch an event.




<mx:Script><![CDATA[

        import mx.controls.Alert;

        private function createListener(e:Event):void {

            b1.addEventListener(MouseEvent.MOUSE_OVER, myEventHandler);

            b1.addEventListener(MouseEvent.CLICK, myClickHandler);

        }

        private function myEventHandler(e:Event):void {

           var result:Boolean = b1.dispatchEvent(new   

                MouseEvent(MouseEvent.CLICK, true, false));           

        }

        private function myClickHandler(e:Event):void {

            Alert.show("The event dispatched by the MOUSE_OVER was of 

                    type '" + e.type + "'.");

        }

  ]]></mx:Script>

  <mx:Button id="b1" label="Click Me"/>






Event can also be manually dispatched from mxml.




<mx:Script><![CDATA[

        import mx.controls.Alert;

        private function createListener(e:Event):void {

            b1.addEventListener(MouseEvent.CLICK, myClickHandler);

        }

        private function myClickHandler(e:Event):void {

            Alert.show("The event dispatched by the MOUSE_OVER was of 

                    type '" + e.type + "'.");

        }

    ]]></mx:Script>

    <mx:Button id="b1" label="Click Me" mouseOver="b1.dispatchEvent(

            new MouseEvent(MouseEvent.CLICK, true, false));"/>








Question: What is the signature of addEventListener() function?
Answer: The signature is –





componentInstance.addEventListener(

    event_type:String, 

    event_listener:Function,

    use_capture:Boolean, 

    priority:int, 

    weakRef:Boolean

)

event_type– Type of event, can be a String or a static constant such as MouseEvent.CLICK

event_listener – The event handler function.

use_capture– This controls the phase in event flow when the listener will be active. If its true the listener is active during capture phase, else in targeting and bubbling phase.

priority– higher the value, sooner the listener will be executed.Default value is zero but cane be set to negative/positive integer value.

weakRef- A Strong reference prevents the listener from being garbage collected and a weak does not.





If an object registers a listener for an event, it assigns a reference to it in internal array known as a listener list and maintains that reference until the listener is unregistered explicitly via the removeEventListener() method.




package{

   import flash.display.*;

   import flash.events.*;

   public class AnonymousListener extends Sprite {

     public function AnonymousListener(){

         stage.addEventListener(MouseEvent.MOUSE_MOVE,

                                function(e:MouseEvent):void

                                {

                                    trace(“mouse move”);

                                });

     }

   }

}





Here the anonymous function is permanently stranded in the Stage instance’s listener list. The program cannot unregister the anonymous function because it has no reference to that function. Stranded listeners are a big problem and cause memory waste and other side effects in AS.

Question: What exactly is the importance of weakRef parameter in addEventListener()?
Answer: We know that when an object registers a listener for an event it needs to be explicitly unregistered. If we set a listener with useWeakReference set to true it prevents that listener from becoming stranded in the listener list of the object with which it registered. For example suppose  an Object obj registers a listener list for an event evt. Also suppose that the only reference the program has to list is the one held by obj. Normally list would be held by obj until list is unregistered for the event evt. However as list was originally registered with useWeakReference set to true, and because obj holds the only remaining reference to list in the program, list becomes eligible for garbage collection. So GC can choose to remove list from obj’s listener list and delete it from memory.
Now can we say we can set useWeakReference to true for all the listeners and need not to remove them manually? Well answer is NO. We don’t know when will GC be called and also our application may not use enough memory to trigger a garbage collection. As a result, the listener will continue to be executed anytime that particular event is dispatched. So it should not be relied on as a way to automatically remove event listeners.
http://gskinner.com/blog/archives/2006/07/as3_weakly_refe.html
http://codeofdoom.com/wordpress/2009/03/02/an-indepth-look-at-flex-events/
http://scrtchpad.wordpress.com/2008/04/14/flex-best-practices/

Question: Can we pass additional parameters to event handler while using addEventListener()?
Answer: We can pass additional parameters depending upon how we add the listeners. If we define our listener using addEventListener() we cannot arbitrarily add new parameters, so the following is wrong:


  public function addListeners():void {
     b1.addEventListener(MouseEvent.CLICK,clickListener);
  }
  public function clickListener(e:MouseEvent, a:String):void {
 ...
}

But we can define a pass-through method like:
   
   private var specialParam1:String;
   private var specialParam2:String = "42";
   private function initApp(e:Event):void {
        assignSpecialParam(e);
   /* Change the value of specialParam whenever the user changes it in
    the TextInput and clicks the button. */  
    ti1.addEventListener("focusOut", assignSpecialParam);
  
   /* Define the pass-through method in the addEventListener() method
   call. You can add any number of parameters, as long as teh target
   method's signature agrees. */
   myButton.addEventListener(MouseEvent.CLICK,function(e:
      MouseEvent):void { myClickListener(e, specialParam1,   
          specialParam2); } );
  }
  private function assignSpecialParam(e:Event):void {
     specialParam1 = ti1.text;
  }
  /* This method acts as the event listener, and it has any number of
  parameters that we defined in the addEventListener() call. */
  private function myClickListener(e:MouseEvent, s1:String,
            s2:String): void {
     myButton.label = s1 + " " + s2; } ]]>
 
 


Question: What is the signature of removeEventListener() function?
Answer:
componentInstance.removeEventListener(event_type:String, 
listener_function:Function, use_capture:Boolean)
We can listen for events during all event phases by callign addEventListener() twice: once with use_capture to true and then again with false. To remove both we call removeEventListener() twice with with use_capture set to true and false. We can only  remove the event handlers added with addEventListener() method in ActionScript code. We cannot remove event handler that was defined in MXML tag.

Second part of this series is available here.

4 comments:

Anonymous said...

This block is really very helpful,Thanks a lot for such efforts.

Anonymous said...

its really a very good blog and i come to know while searching for some interview que for flex ...nice to come across this, it might be a fav blog for me from this time onward.

Akhil Mittal said...

Thanks!! Keep visiting for new questions!!

Anonymous said...

You have done a wonderful work for flex and java crowd buddy......thanks for your such a nice thought which u have accomplished for flex ppl

......Gaurav