Go Ask Your Container

First in a series of five. This blog entry is part of my article in the Flash and Flex Magazine

One of the things I had to learn moving from Windows programming to Flash/ActionScript programming was all the asynchronous patterns (aka two-part procedures). If one needs to load a resource in the middle of a process, one cannot just call a loader and wait for the results before continuing. One has to register an event listener to the loader object and put the remainder of the process into that listener function. When the loader is done loading, it calls the listener, and the rest of the process goes on its merry way. While this is indeed asynchronous programming, events themselves are not asynchronous.

Events are dispatched (aka “fired”, “launched”, “broadcast”, “thrown”) from classes implementing IEventDispatcher – usually by descending from or containing the class EventDispatcher – by calling the event handler functions (“listeners”) previously registered. The dispatcher does not care how many listeners have registered themselves or what they do: it just calls every one registered for a particular message.

Event handlers are, at their core, callback functions. One can call them explicitly, but they are designed to be registered with a dispatcher, and wait for it to call them. One should keep these callback functions very lightweight because events themselves are not asynchronous.

When one to fires an event by calling a dispatcher’s dispatchEvent method, the method literates through the list of registered listener functions, calling each one with the Event object as a parameter, and waiting for it to return before calling the next listener function. Then the dispatchEvent method itself returns. The code that calls dispatchEvent can assume that, when that call returns, all the listeners have had a chance to see and modify the Event object.

One can use events as a bidirectional messages between the dispatcher and the listeners, not just a one-way notification system; the event object can receive information from the listeners as well as send information. Combined with bubbling events that allow containers to get events without registering with the originating dispatcher, this patterns allows nested objects like ItemRenderers to ask questions of their high-level containers without each intermediate container passing the events up and the results down. This relationship is a loosely-linked one: the dispatcher does not know what object is setting the answer properties of the Event object; the listener does not know what object is dispatching the Event object. The form can add or remove containers without breaking.

The Event class already has one Boolean property that is designed for listeners to set: the defaultPrevented property. Listeners can call event.preventDefault() to set this, and the dispatching code can check the value (the dispatchEvent method itself returns this value as well).

For example, an ItemRenderer needs to know if the data it is displaying is part of the set that the user wants to focus on. The data itself does not know, but the top-level form object does. The ItemRenderer can dispatch an event containing its data object and a property for the current status.

public class AmIInUserFocusEvent extends Event
{ 
    public function AmIInUserFocusEvent(type : String = AM_I_IN_FOCUS, bubbles : Boolean = true, cancelable : Boolean = false) 
    { 
        super(type, bubbles, cancelable);
    }

    public var rowData : RowData; public var isInUserFocus : Boolean; 
}

In the ItemRenderer:

 ...
       var newEvent : AmIInUserFocusEvent = new AmIInUserFocusEvent(AM_I_IN_FOCUS, true);
       newEvent.rowData = this.data as RowData;

       this.dispatchEvent(newEvent); // ---- listeners execute here 

       if (newEvent.isInUserFocus) 
           text.styleName = "focusStyle"; 
       else 
           text.styleName = "neglectedStyle"; 
 ...

In some container that holds the item renderer’s list or grid :

 ... 
        this.addEventListener(AM_I_IN_FOCUS, onFocusQuestion);

        ....

    private function onFocusQuestion(event : AmIInUserFocusEvent) : void
    {
        event.isInUserFocus = focusList.contains(event.rowData); 
    }
 ...

To summarize, in additional to sending notifications and data out, events can also get answers. ActionScript’s built-in event framework allows loosely-coupled communication between different parts of a form, even across component bounds.

(Update: sample application in the final installment)

7 thoughts on “Go Ask Your Container

    1. Put the id’s for those rows in the list to begin with. As each Item Renderer initializes, it will receive its checked state.

      This is how a search result will show the correct checked state for items selected in a separate browse list, for example.

      Cheers

Leave a Reply

Your email address will not be published. Required fields are marked *