Thursday, February 9, 2012

Flex Events (part 2)


Question: What is an EventDispatcher class?
Answer: Every object can be traced to DisplayObject and that class inherits from the EventDispatcher class. This is a base class that provides important event model functionality for every object on display list. The EventDispatcher class implements the IEventDispatcher interface. This allows us to create custom classes that cannot inherit from EventDispatcher or one of its subclasses to implement the IEventDispatcher interface to gain access to its methods. The addEventListener() method of this class is used to register event listeners. Several other methods of the EventDispatcher class provide useful information about the existence of event listeners. The hasEventListener() method returns true if an event listener is found for that specific event type on a particular display list object. The willTrigger() method checks for event listeners on a particular display list object, but it also checks for listeners on all of that display list object’s ancestors for all phases of the event flow. The method returns true if it finds one

Question: How to define an inner event listener or closure function?
Answer:


<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 

xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:s="library://ns.adobe.com/flex/spark" creationComplete="initApp()">

 <fx:Script> <![CDATA[

    import mx.controls.Alert;

    private function initApp():void {

      b1.addEventListener("click",

       function(e:Event):void { 

         Alert.show("The button was clicked."); 

       }

      );

    }

 ]]> </fx:Script> 

<s:Button id='b1' label="Click Me"/></s:Application>

Function closures are created anytime a function is executed apart from an object/class. They retain the scope in which they are defined and this leads to unexpected results some times.


<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 

xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:s="library://ns.adobe.com/flex/spark" creationComplete="foo()"> 

<s:layout> <s:VerticalLayout/> </s:layout> 

<fx:Script> <![CDATA[

  [Bindable] 

  private var answer:String; 

  private function foo():Function { 

     var x:int = int(ti1.text); 

     function rectArea(y:int):int {

     // function closure defined 

     return x * y; 

     } 

   return rectArea; 

  }

  private function bar():void {

     var x:int = 2;// ignored 

     var y:int = 4; // ignored 

     var myProduct:Function = foo(); 

     answer = myProduct(int(ti2.text)); // function closure called } ]]> </fx:Script> 

<mx:Form width="107">

  <mx:FormItem label="X"> 

    <mx:TextInput id="ti1" text="10" width="37" textAlign="right"/>   

  </mx:FormItem> 

  <mx:FormItem label="Y" width="71"> 

     <mx:TextInput id="ti2" text="20" width="38" textAlign="right"/> 

  </mx:FormItem> 

  <mx:Label id="label1" text="{answer}" width="71" textAlign="right"/> 

 </mx:Form> 

 <s:Button id='b1' label="Compute Product" click="bar()"/> </s:Application>

The function bar() gets the function myProduct() returned by the function foo(). Now for returned function the local variables are x=2 and y=4. But when that function is called it still has x=10 which means it retains the variable.

If the listener we pass to addEventListener () is a nested inner function, we should not pass true for useWeakReference. The reason is for Flex inner function is an object and can be freed by garbage collector. If the value of useWeakReference is true then there is a possibility that GC might free the function and the function will not be called when the event is triggered.

Question: How to create event handler classes?
Answer: We can create an external file and use its methods to handle events. This makes MXML application more readable and maintainable.


package {

   import flash.events.Event;

   import mx.controls.Alert;

   public class MyEventHandler {

     public function MyEventHandler() {

    // Empty constructor. 

   }

   public function handleAllEvents(event:Event):void {

     Alert.show("Some event happened."); 

   }

 }

}
Then we can use this method in MXML to handle all the events like:

private var myListener:MyEventHandler = new MyEventHandler();
private function createHandler():void
{ b1.addEventListener(MouseEvent.CLICK, myListener.handleAllEvents); }

But the best way is to declare method as static so that we need not to instantiate the class.

Question: When should we use Event metadata tag?
Answer: We use this metadata tag for the following purposes.
ActionScript components – Above the class definition, but with in the package definition, so that the events are bound to the class and not a particular member of the class.

MXML components – In the tag of a MXML file.
[Event(name="eventName", type="package.eventType")]
The following example identifies the enableChange event as an event that an actionscript component can dispatch



[Event(name="enableChange", type="myEvents.EnableChangeEvent")]

public class MyComponent extends TextArea

{

    ...

}
The following example shows the [Event] metadata tag with in tag of an MXML file:


<?xml version="1.0"?>

<!-- events\myComponents\MyButton.mxml -->

<mx:Button xmlns:mx="http://www.adobe.com/2006/mxml" 

    click="dispatchEvent(new EnableChangeEvent('enableChanged'));">

    <mx:Script>

        <![CDATA[

            import myEvents.EnableChangeEvent;

        ]]>

    </mx:Script>

    <mx:Metadata>

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

    </mx:Metadata>

</mx:Button>

Now this can be used in application as shown:


<?xml version="1.0"?>

<!-- events/MainEventApp.mxml -->

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 

    xmlns:MyComp="myComponents.*" >

    <mx:Script>

        <![CDATA[

            import myEvents.EnableChangeEvent;

            public function enableChangedListener(eventObj: 

                    EnableChangeEvent):void {

                    // Handle event.

            }

        ]]>

    </mx:Script>

    <MyComp:MyButton enableChanged="myTA.text='got event';" />

    <mx:TextArea id="myTA" />

</mx:Application>

Question: What does calling preventDefault() on an event do? How is this enforced?
Answer: When a user types into a text field, the default behavior is that the character is displayed in the text field. Because the TextEvent.TEXT_INPUT event's default behavior can be canceled, we can use the preventDefault() method to prevent the character from appearing. An example of a behavior that is not cancelable is the default behavior associated with the Event.REMOVED event, which is generated whenever Flash Player is about to remove a display object from the display list.
We can use the Event.cancelable property to check whether we can prevent the default behavior associated with a particular event. If the value of Event.cancelable is true, then preventDefault() can be used to cancel the event; otherwise, preventDefault() has no effect.


<mx:Script>

<![CDATA[

private function textArea_textInput(evt:TextEvent):void {

if (evt.text == "\n") {

evt.preventDefault();

}

}

]]>

</mx:Script>

<mx:TextArea id="textArea" verticalScrollPolicy="on" width="160"

height="120" textInput="textArea_textInput(event);">

<mx:text>The quick brown fox jumped over the lazy dog.</mx:text>

</mx:TextArea>

To determine whether an event currently being dispatched has had its default behavior prevented, we can check the return value of the Event class’s instance method isDefaultPrevented() within a listener registered for that event.

Question: How can we stop the event propagation?
Answer: We can stop traversal of display list during any phase, by calling any one of the two methods: stopPropagation() and stopImmediatePropagation(). Both methods prevent an Event object from continuing on its way thorough the event flow. However there is a slight difference about whether the remaining event listeners of the current node are allowed to execute. The stopPropagation() method prevents the Event object from moving on to the next node, but only after any other event listeners on the current node are allowed to execute. The stopImmediatePropagation() method also prevents the Event objects from moving on to the next node, but it does not allow any other event listeners on the current node to execute.

Question: What is custom event? What is clone() method in Event class?
Answer: A custom event must override clone() and toString() method. The Event.clone() returns a cloned copy of the event by setting type property and any other new property. We define this method to return an event instance created with the new operator.


package myEvents

{

    //events/myEvents/EnableChangeEvent.as

    import flash.events.Event;

    public class EnableChangeEvent extends Event

    {

        public static const ENABLE_CHANGED:String = "enableChanged";

        public var isEnabled:Boolean;




        public function EnableChangeEvent(type:String,

                        bubbles:Boolean = false,

                        cancellable:Boolean = false 

                        isEnabled:Boolean=false) {

                 super(type,bubble,cancellable);

                 this.isEnabled = isEnabled;

        }




        // Override the inherited clone() method.

        override public function clone():Event {

            return new EnableChangeEvent(type, isEnabled);

        }

        //Every custom event class must override toString(). Note that

        // “eventPhase” is an instance variable relating to the event

        // flow.

        override public function toString():String{

         return formatToString(“EnableChangeEvent”,“bubbles”, 

            “cancellable”, “eventPhase”, “isEnabled”);

        }

    }

}

Now still the question is what exactly is the advantage of clone()? Actually if we have a situation that after catching the event in its registered listener, we want to dispatch this event again then a new instance of this event wont be created else every re-dispatch will result into a new instance.

Question: Can we register a single event handler for multiple components? Can we have multiple listeners for single event?
Answer: Yes, we can register single event handler for multiple components in MXML and AS.




<mx:Button id="b1" label="Click Me" click="submitForm(event)"/>

<mx:Button id="b2" label="Click Me, Too"  click="submitForm(event)"/>

public function createHandlers(e:Event):void {

            b1.addEventListener(MouseEvent.CLICK, submitForm);

            b2.addEventListener(MouseEvent.CLICK, submitForm);

        }

 We can also define multiple listeners for a single event.


<mx:Script><![CDATA[

        [Bindable]

        private var s:String = "";

        private function submitForm(e:Event):void {

            // Handle event here.

            s += "The submitForm() method was called. ";

        }

        private function debugMessage(e:Event):void {

            // Handle event here.

            s += "The debugMessage() method was called. ";

        }

    ]]></mx:Script>

      <mx:Button id="b1" label="Do Both Actions"

        click='submitForm(event); debugMessage(event);' 

    />



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

    <mx:Script><![CDATA[

        [Bindable]

        private var s:String = "";

        public function createHandlers(e:Event):void {

            b1.addEventListener(MouseEvent.CLICK, submitForm);

            b1.addEventListener(MouseEvent.CLICK, debugMessage);

        }

        private function submitForm(e:Event):void {

            // Handle event here.

            s += "The submitForm() method was called. ";

            

        }

        private function debugMessage(e:Event):void {

            // Handle event here.

            s += "The debugMessage() method was called. ";            

        }

    ]]></mx:Script>




    <mx:Button id="b1" label="Do Both Actions"/>

    <mx:Label id="l1" text="{s}"/>

    <mx:Button id="b2" label="Reset" click="s='';"/>

</mx:Application>

Question: What is display list?
Answer: All visual objects (including Flex containers and controls) are subclasses of the DisplayObject class. They are in a tree of visible objects and make up the application. The root of the tree is Stage; below it is SystemManager object and then the Application Object. Child containers and controls are leaf nodes of the tree. That tree is known as display list. The term display list object and node are used interchangeably.

Question: If we want to write highly cohesive flex components then how can we pass any parameter along with Event and we don’t want to use CustomEvent.
Answer: We can use data property of Event class.

Question: What is the difference between creationComplete and mx.Application.applicationComplete events?
Answer: Actually when creationComplete is called stage object is null. Stage is not available creationComplete() is called rather Stage is only available when called with updateComplete() and applicationComplete(). The creation order is different for containers and components because a container can be parent of other components or containers.

So the order in which the events are dispatched by the application are preinitialize(), initialize(), creationComplete(), updateComplete() and applicationComplete().
preinitialize() is dispatched when the component has been attached to its parent container, but before the component has been initialized, or any of its children have been created. In most cases, this event is dispatched too early for an application to use to configure a component.
initialize() is dispatched when a component has finished its construction and its initialization properties have been set. At this point, all of the component’s immediate children have been created (they have at least dispatched their preinitialize() event), but they have not been laid out. Exactly when initialize events are dispatched depends on the container’s creation policy, as described later in this section.
creationComplete() is dispatched when the component, and all of its child components, and all of their children, and so on have been created, laid out, and are visible.
updateComplete() is dispatched every time the container or component characteristics are changed.
applicationComplete() dispatches events after the Application has been initialized, processed by the LayoutManager and added to the display list. This is the last event dispatched during an application startup. It is later than the application’s creationComplete() event, which gets dispatched before the preloader has been removed and the application has been attached to the display list.
Another solution for this is using the callLater() function.The callLater() function queues the operation to be executed at next render event.

3 comments:

FlexProg said...

Good for interview preparation! Keep it up man!

Anonymous said...

Very useful information. In my opinion, the only thing may not be correct is about call later() method as it don't
delay code execution until the user interface is updated.
The callLater() method queues an operation to be performed for the next screen refresh, rather than in the current update.
The callLater() method is most commonly used with the creationComplete event to ensure that a component has finished being created before Flex proceeds with a specified method call on that component.

Unknown said...

@Anonymous you are right that it delays it for next render event.