Category Archives: Flex/ActionScript

Lazy, Asynchronous Binding, or Call Once, Return Twice

The best part of binding is that it just works: your visible control (the target) gets the data (the source) to show without worrying about synchronizing it with the various steps of data retrieval.

“Getting the value” means retrieving a field, function, or accessor of an object. The target will always get the value when it is initialized in the Configuration phase (so const values work as binding sources even without the [Bindable] metatag).

The Bindable Mantra

“Hear the event, get the value”

The important and subtle implication of this is that the notification dispatcher and the (presumably changed) data are not necessarily the same thing. The normal and default behavior is that changing a value will dispatch an event to the targets bound to that value. This is the built-in behavior when properties use the [Bindable] metatag without specifying an event type; the compiler creates hidden accessors and mutators that checks for changed values (optimizing by ignoring new values that are the same as the old value) and dispatches a PROPERTY_CHANGED event.

The things that bind to the bindable property get a hidden event listener that listens for the PROPERTY_CHANGED event and assigns the source to the target whenever the listener gets an event.

Simple Binding

Target Object: a form

<Application />
    <s:List dataProvider="{foo.listData}" />
</Application>

Source Object: a data class (e.g. a model)

class Foo extends EventDispatcher
{
    [Bindable]
    public var listData : ICollection;
}

Specified Event Binding:[Bindable("foo")]

If the [Bindable] metatag specifies an event type (e.g [Bindable("event type")], then the compiler does not create hidden accessors and mutators for the property; the class must dispatch a notification event itself. One can use this to make non-accessor functions bindable: the hidden event listeners created for the targets will call the function whenever they hear the event and assign the result to the target.

A class can have many properties marked as [Bindable("foo")], and whenever it dispatches new Event("foo"), everything bound to any of those properties will retrieve the data value immediately.

Target Object: a form

<Application />
<!--   call get listData() on initialization and whenever "listDataChange" heard  -->
    <s:List dataProvider="{foo.listData}" />

<!--   call get listCount() on initialization and whenever "listDataChange" heard  -->
    <s:Label text="{foo.getListCount().toString()}" />   
</Application>

Source Object: a data class (e.g. a model)

class Foo extends EventDispatcher
{
    public static const LIST_DATA_CHANGE : String = "listDataChange";
    public static const TOTAL_COUNT_CHANGE : String = "totalCountChange";

    [Bindable("listDataChange")]
    public function get listData() : ICollection
    {
        return _listData;
    }

    public function setListData(value : ICollection) : void
    {
        if (_listData != value)
        {
            _listData = value;
            dispatchEvent(new Event(LIST_DATA_CHANGE));
        }
    }

    [Bindable("listDataChange")]
    public function getListCount() : int
    {
        if (_listData == null)
            return 0;
        else
            return _listData.length;
    }

    [Bindable("listDataChange")]
    [Bindable("totalCountChange")]    // something else dispatches this event
    public function totalCount() : int
    {
        return getListCount() + someOtherNumber;
    }
}

The Cool Bit

Because the notification can be separate from the actual data, one can return result asynchronously to a bound listener. This might be called the Data Accessor Object Pattern.

1. The target calls the source accessor
2. If the underlying object is uninitialized or stale

a. The accessor immediately returns null(which is a perfectly reasonable return value)

b. The accessor sends a request to a server

c. When the server returns data, the responder dispatches the binding event

d. The target hears the binding event and calls the accessor

3. The accessor returns the new data

Target Object: a form

<Application />
 <!--  call get listData() on initialization and whenever "listDataChange" heard -->
    <s:List dataProvider="{foo.listData}" />  
</Application>

Source Object: a data class (e.g. a model)

class Foo extends EventDispatcher
{
    public static const LIST_DATA_CHANGE : String = "listDataChange";

    [Bindable("listDataChange")]
    public function get listData() : ICollection
    {
        if (_listData == null)
        {
            var serverCall = new ServerCall(receiveListData);

            //  return null immediately; don't wait for the server
        }

        return _listData;
    }

    public function receiveListData(data : ICollection) : void
    {
        if (_listData != data)
        {
            _listData = data;

                     // make the bound targets call listData() AGAIN
            dispatchEvent(new Event(LIST_DATA_CHANGE));
        }
    }
}

… Only one, but the Collection has to want to change

Using an ArrayCollection as the data provider for a list (or any display object) is easy and powerful. Somehow, collection variables fire change events and update their binding targets not only when the whole list variable changes, but also when any element in that list changes. Lists and Grids (which love ArrayCollections) explicitly listen for COLLECTION_CHANGE events from inside their dataProvider as well as wholesale changes.

Note that only the dataProvider seems to do this: binding an ArrayCollection to any other property will not automatically get notifications when a collection item changes (although binding to arrayCollection.length will). If you use BindingUtils or variable injection to a mutator, it will only get updated when the whole value changes, not when an item changes.

Given that the elements know nothing about the Collection containing them (indeed, an object can be contained by several Collections and Arrays simultaneously), and the Collection cannot require that elements descend from some sort of CollectionItem ancestor, or implement a specific ICollectionItem interface, or even call a method in the Collection when they change, the only way the Collection can monitor elements is if it takes advantage of a protocol that most objects do anyway: the Bindable/PROPERTY_CHANGE event.

The ArrayCollection does not, despite appearances, wrap an Array; it wraps an ArrayList that wraps an Array. The ArrayList monitors every element in its array by adding a listener for PropertyChangeEvent.PROPERTY_CHANGE events on every array element that happens to implement the IEventDispatcher interface. A listener can listen for any type of event it wants to without being dependent on the event dispatcher; they are loosely coupled. As it happens, any class with a [Bindable] property is automatically an IEventDispatcher, and it dispatches the PROPERTY_CHANGE event every time a [Bindable] property changes. The class can also dispatch this event explicitly.

The ArrayList listens for these events and dispatches a COLLECTION_CHANGE event summarizing and describing the changes to the Array’s items. The ArrayCollection uses this event from the ArrayList to gather information for its own COLLECTION_CHANGE event, and the List or Grid uses the COLLECTION_CHANGE event to refresh or rebuild their ItemRenderers.

To be clear: array element objects fire PROPERTY_CHANGE events automatically for every change to their [Bindable] properties. If you want properties that are not bound or are bound to explicit event ([Bindable("readOnlyPropertyChange")]) to trigger COLLECTION_CHANGE events in the collections that hold them, those property mutators have to explicitly fire the PROPERTY_CHANGE event (in addition to any other events they dispatch).

[Bindable]
public function get foo() : String
{
    return _foo;
}
public function set foo(value : String) : void
{
    _foo = value;
}

[Bindable("readOnlyPropertyChange")]
public function get readOnlyProperty() : int
{
    return _readOnlyProperty;
}

public function clearBar() : void
{
    var oldValue : String = _readOnlyProperty;
    _readOnlyProperty = 0;

//    signal for binding/listeners directly on this object
    this.dispatchEvent(new Event("readOnlyPropertyChange"));

//    signal for binding/listeners on this object's Collection
    if (hasEventListener(PropertyChangeEvent.PROPERTY_CHANGE ))
    {
        var event : PropertyChangeEvent =
                PropertyChangeEvent.createUpdateEvent( this, "property", oldValue, 0);
        dispatchEvent( event );
    }
}

Arrays do not detect or fire events; they are just a collection of items.

Sequence diagram of the events between Lists, ArrayCollections, ArrayLists, Arrays, and the items in Arrays
Sequence diagram of the events between Lists, ArrayCollections, ArrayLists, Arrays, and the items in Arrays

All AIR Applications Are Single-Instance

This is not an option: if one tries to launch an AIR application twice, the first instance remains and no other instances start. The first application, however, does get notification and the command-line arguments of the subsequent application executions. It’s a subtle way of communicating with a running AIR application.

If an application has to handle multiple instanciation (like a registered file reader), it has to be able to present multiple instances of some part of its user interface (or be willing to replace the current data at any time). This hearkens back to the days of Multi-Document Interface (MDI) applications. One can encapsulate the main UI as a component, and the application can create one for each “instance” the application needs to present.

Multiple Invocations of an AIR ApplicationThe NativeWindow.invoke Event Fires On Every Application Launch

The application will get an event every time the OS launches an instance of the application. It gets an event on startup, and it gets one each time the OS executes the AIR application file; subsequent executions do not start additional  instances. These events contains the command-line parameters specific to that invocation.

The Invoke event fits into the startup cycle here:

  1. FlexEvent.ADD for the application object
  2. FlexEvent.PREINITIALIZE
  3. Event.ADDED for the descendents of the application object
    (These events happen sporadically intermixed with the following events)
  4. FlexEvent.INITIALIZE
  5. FlexEvent.CREATION_COMPLETE
  6. Event.ADDED_TO_STAGE
  7. FlexEvent.APPLICATION_COMPLETE
  8. InvokeEvent.INVOKE
  9. Event.ACTIVATE

Command-line Parameters in AIR Applications

First, the simple behavior:

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx"
    invoke="onInvoke(event)">
    <fx:Script>
        <![CDATA[
            private function onInvoke(event : InvokeEvent) : void
            {
                logText.text += "invoke: event.arguments = " + 
                    event.arguments.toString();

                if (event.currentDirectory != null) {
                    logText.text += "; event.currentDirectory = " + 
                        event.currentDirectory.nativePath;
                }
                else
                    logText.text += "; event.currentDirectory = null";


                if (event.reason != null)
                    logText.text += "; event.reason = " + event.reason;
                else
                    logText.text += "; event.reason =  null";
            }
        ]]>
    </fx:Script>

    <s:TextArea id="logText" left="10" right="10" top="105" bottom="10" />
</s:WindowedApplication>

 

Things My Event Told Me

  • InvokeEvent.arguments is an array (never null) of strings. See your operating system for the rules about special characters and quoting.
  • InvokeEvent.currentDirectory is a File instance set to the directory of the executable. Note that running from the IDE will point to the FlashBuilder.exe directory; running from a shortcut/alias will indicate the location of the shortcut, not the .air file.
  • InvokeEvent.reason is either “standard” or “login” if the  OS starts it automatically (see InvokeEventReason for constants)

Things My Event Never Told Me

  • The event does not indicate if this event is part of the application startup (i.e. the first event) or a subsequent invocation. Use a global counter.
  • The event does not indicate if this event is because the OS registered this application for a file type and the user “opened” a file of that type. The sole argument is the complete (native) path including file name, but shortcuts and the command-line can start the application can have a single argument that is a file path as well.

 

It’s an Interface; It’s a Code-Behind; It’s a … Skin?

I managed to avoid skinning components in Flex 3 (I called it “dancing kitties”). I just made the components work. I tried to ignore the changes in Flex 4 skins, but Spark components are very different than MX components, although Adobe has succeeded in making the changes apparently minor.

In Flex 2 and 3, “skins” were CSS styles and images and, rarely, bits of code that created graphics.

What Flex 4 Skins Are

Flex 4 skins are ActionScript classes that are completely responsible for presenting the data from another ActionScript class.  Custom components can inherent behavior from their class ancestors (UIComponent, Group, etc.), but builds their appearance by choosing or creating a custom skin class. The component has the properties and methods to manage the component’s behavior and data, and the skin class presents it to the user. A skin class usually descends from the ancestor class Skin (itself a child of Group) and contains other components (e.g. Labels, Images, RichTextEdit) to display and gather data, (somewhat like the way a custom item-renderer contains other components). The nesting can seem a little recursive, but it has the same advantages as any other object oriented protocol: each object does its thing with loose linkages to other objects.

It’s Code-Behind

The relationship between a component and its skin is like a code-behind pattern: specified component class variables refer to the skin class’ sub-components by name. As the skin class constructs, it passes each of its sub-compents (e.g. Buttons) to the host component. The host component does two things: first, if it finds a variable annotated with a [SkinPart], it assigns the instance to the value from the skin. Second, it calls the addPart() method so the component can add event listeners, etc. For example, if a skin class used with a Button has a sub-component with the id “labelDisplay”, that sub-component’s “text” property will get the Button.label value via the ButtonBase.setContent() method:

The Component The Skin
[SkinPart(required="false")]
public var labelDisplay : IDisplayText;   // must have a .text property
<s:Label id="labelDisplay" />
protected function setContent(value : String) : void
{
    _content = value;  //    this stores the value in the component

    if (labelDisplay != null)  // it's not required, so it could be null
        labelDisplay.text = value;
}
The Label component displays the new value

It’s an interface

And I mean with a lower-case “i”: it’s is not an ActionScript Interface It is an agreement between the component and the skin so they can work together (actually, the component requires and the skin fulfills):

  • [SkinPart(required="true")] or [SkinPart]on the component requires the skin to have a sub-component with a specific name; the class will throw an exception during creation without it
  • [SkinPart(required="false")] on the component will connect to a skin’s sub-component with a specific name if it’s present

It’s a View

So skins have evolved from being styles and bitmaps to more of a MVC-type view, and the component acts as a controller and model. The component keeps a copy of all the visible data (like label texts) so it can change and initialize skins at any time. When a skin initializes, the component gets a notification for each sub-component of the skin object, and the component can add an event listener or set its value. While the component can query the skin about constraint-based position and sizes, the less the component knows about the view, the better.

Likewise, the less the skin knows about the component, the better. The component handles all the interaction with the outside world: methods to set and filter the data, validation of property values, etc. The skin gets data values and occasionally styles and top-level dimensions. The component uses its code-behind references directly to the skin’s sub-components to set property values and get events.

Binding Optional

While binding is very cool, this protocol does not usually use events and watchers and binding stuff: the code in the component explicitly assigns values to its references of the skin’s sub-components (e.g. SkinnableTextBase.setPrompt()). The skin can bind to the component’s properties (e.g. hostComponent.title), but it’s unusual.

The Component’s View of the Skin

These are the states, properties, and methods of the component that give it access to and control over the skin.

skin class
(will inherit from ancestor classes)
.skinClass
required skin states [SkinState("xxx")]
skin sub-components
[SkinPart(required="true")]
public var closeIcon : Button;

[SkinPart(required="false")]
public var labelDisplay : IDisplayText;   // must have a .text property
add/remove event listeners to skin objects
override protected function partAdded(partName : String, instance : Object) : void
{
    super.partAdded(partName,instance);
    if (instance == closeIcon)
    {
        closeIcon.addEventListener(MouseEvent.CLICK, onCloseIcon);
        closeIcon.source = getStyle(“icon”);
    }
}

override protected function partRemoved(partName : String, instance:Object) : void
{
    super.partRemoved(partName, instance);

    if (instance == closeIcon)
        closeIcon.removeEventListener(MouseEvent.CLICK, onCloseIcon);
}
skin instance
(rarely used)
.skin
supporting methods
SkinnableComponent.invalidateSkinState();
// this forces a call to getCurrentSkinState()
other miscellaneous overrides
override protected function getCurrentSkinState() : String { 

//    translate component states into skin states
//    note: the base class' method, 
//            SkinnableComponent.getCurrentSkinState(), returns null
}

override public function styleChanged(styleProp : String) : void
{
    super.styleChanged(styleProp);

    if (styleProp == “icon”)
        closeIcon.source = getStyle(“icon”);
}
data values push values into the skin using code-behind
currentState the component sets the skin’s state to reflect some aspect (e.g. disabled)

The Skin’s View of the Component

These are the properties the skin used to receive and communicate with the host. In general, the skin assumes that the synchronization with the component will push values into the sub-components and change the skin’s state; it does not reference the .hostComponent directly.

host class [HostComponent("spark.components.Button")]
host instance
(rarely used)
.hostComponent
data values pushed into values by the component
currentState the component sets the skin’s state to reflect some aspect (e.g. disabled)

Skin State

The component can require specific skin states (e.g. “normal”, “disabled”). It uses those states to instruct the skin to change appearance, but leaves the specifics up to the skin. Adobe could have used an event, an expected method, or some other way to signal the skin; the advantage of using states is that it’s easy to set up an MXML component to respond to states. The base component’s invalidateSkinState() method (called when the component’s state change, for example) triggers a call to getCurrentSkinState(). That method examines the component’s properties and returns the skin state that fits.

For example, SkinnableTextBase requires the skin to have states “normalWithPrompt” and “disabledWithPrompt”, and uses them as appropriate in getCurrentSkinState() to have the skin show the .prompt text (using whatever sub-component the skin chooses to show it).

Object-Oriented Skins … Or Not

Adobe has been uncharacteristically forceful in their declaration that  we should not create inheritance hierarchies of skin classes; Adobe wants us to copy and paste when creating new skin classes. Part of that is political: they have lots of tools for designers to work with developers, and designers do not sub-class anything. Although I am a OOP-freak, I see their point: skins are so thin (i.e. they have very little behavior implemented in code) that the logistical overhead of having some parts in the ancestor and some in a descendant does not have much benefit to balance it. A well-designed skin class can use CSS as a replaceable properties file, and that can bundle a lot of behavior in a thin class without sub-classes.

 

Hierarchical Data Grids

I recently had cause to create a grid that showed rows with parent, child, and grandchild rows. Each generation’s text is indented to show the relationships, and rows with children have a plus or minus to show and hide them. While the AdvancedDataGrid can create virtual parent rows for unique values, it is not built to show actual parent and child rows.

Sample Application with source: http://flex.santacruzsoftware.com/HierarchicalDataGrids/

One way to manage parent-child relationships is to store all the raw data in one list and use a different list to display the data. Each row must indicate if they are parents and, if so,  are their children are visible. In my case, those flags were already present in the grid’s data row object (ReportRow), alternatively, one could use external lists and messages to hold this information (see The Ultimate Checkbox List Pattern). The row objects have a collection of child rows as a property, and a value that indicates what generation (level) they are.

public function onExpand(event : Event) : void
{
	this.refreshExpandedItems();
}

public function refreshExpandedItems() : void
{
	var prevSelection : Object = reportGrid.selectedItem;
	var prevScrollPosition : uint = reportGrid.verticalScrollPosition;

	this.reportData.refreshDisplayList();

	reportGrid.selectedItem = prevSelection;
	reportGrid.verticalScrollPosition = prevScrollPosition;
}

The grid’s ItemRenderer shows an expansion icon (e.g. “+” and “-“), handles indenting the text to show each row’s level, and throws a bubbling event whenever the user clicks the expansion icon. The dataChange method (or the overriden data mutator) handles indentation as well as which rows should have expansion icons.

private function onDataChange(event : Event) : void
{
	if (this.data != null)
	{
		var theRow : ReportRow = this.data as ReportRow;

		nameLabel.text = theRow.name;
		nameLabel.x = 20 + (theRow.level * INDENT_PER_LEVEL);

		if (theRow.children.length > 0)
		{
			expansionLabel.visible = true;

			if (theRow.isExpanded)
				expansionLabel.text = "-"
			else
				expansionLabel.text = "+"
		}
		else
			expansionLabel.visible = false;
	}
}

private function onExpansionClick(event : Event) : void
{
	var theRow : ReportRow = this.data as ReportRow;

	theRow.isExpanded = !theRow.isExpanded;

	var expandEvent : ExpandEvent =
		new ExpandEvent(ExpandEvent.EXPAND_PARENT, true);
	expandEvent.row = theRow;

	this.dispatchEvent(expandEvent);
}

When this event (or some other application code) marks a row as newly expanded or an expanded row as collapsed, the data object refreshes the entire display list by emptying it and iterating through the entire raw-data list and copying each parent and, if expanded, its children to the display list. Depending on one’s needs, one can recursively check the children for nested grandchildren, etc.

I created a separate class to hold and manage the data as a grid-specific model. The container holding the grid creates an instance of the data object, and that object not only manages the raw and display lists and also parses the result from a server into grid-specific row objects. It also has a bindable array collection of row objects to display; this is what the grid uses as its displayProvider.

While Flex’s update event model will likely avoid thrashing the display, one can use displayList.disableAutoUpdate() or binding with an explicit event and dispatching an explicit event to ensure that the DataGrid refreshes only once after the display list changes

[Bindable(event="displayListChange")]
public var displayList : ArrayCollection = null;

public function refreshDisplayList() : void
{
	displayList.removeAll();

	for(var counter : uint = 0; counter < rawDataList.length; counter++)
		addRow(rawDataList.getItemAt(counter) as RowData);

					//	signal that all changes are complete
	this.displatchEvent(new Event("displayListChange"));

					//	nested function
	function addRow(aRow : RowData, generationNumber : uint = 0) : void
	{
				//	put any client-side filtering tests here

		aRow.displayIndentLevel = generationNumber;
		displayList.addItem(aRow);

		if (aRow.isExpanded)
		{
			var theChildRow : RowData;

			for(var childCounter : uint = 0; childCounter < aRow.children.length; childCounter++)
			{
				theChildRow = aRow.children.getItemAt(childCounter) as RowData;

				addRow(theChildRow, generationNumber + 1);	//	recursive
			}
		}
	}
}

Sorting and Filtering

The raw data list’s sort function needs to sort without breaking the relationship between each row, its parent, and its children. One can either sort the highest level and leave the descendants unsorted; one can sort each of the generations relatives to its siblings. To filter display rows, simply omit rows from the display list rather than using a filter function on either the raw data list or the display list.

Performance

displayList.addItem(aRow) inserts a reference to the row object in the rawDataList ; it does not create a copy of the actual data. Moving a few hundred references around does not take a lot of time. Compared to redrawing the grid itself, managing the references is instantaneous.

Paging Hierarchical Data

Many Flex applications using grids show only part of the total data set (no one actually wants to see 10,000 rows). In this vein, one might want to load the children of a row if and only if the user expands it. The rawDataList will retain any children once downloaded, so repeated expansion and collapse will not overload the server, but getting the child rows the first time requires a specific protocol.

In my case, the server allowed me to get the report data passing flag indicating ParentsOnly (which gets only the top-level rows) or criteria to specify one particular top-level entity (which I used without the ParentsOnly flag to get the descendants). As I don’t get the children until needed, the item renderers need an isParent property in each item (to replace the test theRow.children.length > 0) so knows they whether to show the expansion icon at all.

The EXPAND_PARENT event listener checks the event’s ReportRow isExpanded and children == null If both are true, it has to retrieve the children from the server, insert them into the ReportRow.children property, and then call refreshExpandedItems(). This requires at least one asynchronous step, so the COMPLETE listener (as well as the fault handler) needs to clear any “Please Wait” messages. If the row is collapsing or already has its children, the listener can simply call refreshExpandedItems().

Or That

Coming from Delphi, I use properties for everything. I even create protected and private properties for use inside a class:  they are good idea, so I don’t limit them to public interfaces.

A new wrinkle I’m dealing with is bound properties that have default values. For example, a Button sub-class that has a default caption but also has a public property that the user can bind to some other value. A normal property with a default would look like this:

private static const DEFAULT_ENABLED_CAPTION : String = "Click me!";

[Bindable]
public var enabledCaption : String = DEFAULT_ENABLED_CAPTION;

One can bind this property to a child component in an MXML component or use it in the commitProperties() method of an ActionScript component. It acts as a proxy for the “real” property, and allows different values based on state (e.g. enabled and disabled captions based on the enabled property).

When one uses binding to set values, if the object configuration is null or the property enabledCaption is blank at any time, the property loses its default value and does not get a valid replacement.

    <components:MyButton enabledCaption="{configuration.enabledCaption}"
                  disabledCaption="{configuration.disabledCaption}" />

Assuming that one does not want to allow an empty string (or null), some small changes to the accessor and mutator can handle these automatic assignments by the binding mechanisms:

public static const CAPTION_CHANGE : String = "captionChange";
private static const DEFAULT_ENABLED_CAPTION : String = "Click me!";

[Bindable(event="captionChange")]
public function get enabledCaption() : String
{
    if (_enabledCaption== "")
        return DEFAULT_ENABLED_CAPTION;
    else
        return _enabledCaption;
}
public function set enabledCaption(value : String) : void
{
    if (value == null)
        _enabledCaption = "";    //  strings should not be null

    if (_enabledCaption != value)
    {
        _enabledCaption = value;
        this.dispatchEvent(new Event(CAPTION_CHANGE));
    }
}
private var _enabledCaption : String = DEFAULT_ENABLED_CAPTION;

No matter how many times the binding mechanism updates the component property (i.e. calls the mutator), the accessor will return the default value unless the binding (or some other code) sets the value to a non-null, non-blank string.

Advanced Topics Using a Message-Based Checkbox List Pattern

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

The checkbox pattern described in the previous four blog entries allows a lot of flexibility and subtly in your applications. This post covers some of the issues I’ve dealt with, a bit of philosophical and aesthetic musing, and an example application.

Setting The Manager’s Event Listeners

Setting the  list manager’s listeners in the startup event cycle is tricky. In general, I use addEventListener() in the creationComplete event for listeners on the form, however this does not work if the checkbox list or grid is a design-time  child on the form (e.g. it’s in the MXML or created by createChildren()). By the time the creationComplete event fires, the grid has already initialized and all the item renderers have dispatched their first AmISelected events, which receive no answers because the listener is not set up yet.

Register amISelected listeners in the initialized event.

Messages vs. Interfaces

A different way of loosely linking parts of a system is interfaces. One could pass an instance of an interface that had toggleSection and amISelected methods to an item renderer. The class of that instance could be anything; the caller would not know who was executing the method and the method would not know who was calling it. Passing an instance more than one layer deep, however, creates a loosely-linked chain, and the more links in a chain, the more fragile it is.

Every container between the list manager (e.g. the form or component’s top-level container) would have to pass the instance on to every child that could hold it (probably because the children implemented an interface themselves). Components that had no interest in this interface would still be responsible for passing it on.

Messages, however, are already part of every UIComponent’s interface (because it implements IEventDispatcher): they allow listeners to hook into the messages; the message framework moves the event objects through the containership model; and the event objects themselves can change without changing the interface.

Message-Based MVC

The Ultimate Checkbox List Pattern resembles a Model-View-Controller: the item renderer would be the view; the list of selected items would be the model; and the event listeners on the list manager would be the controller. In an classc MVC pattern, the controller would push the changes into the view, and therefor would need to know how it worked (i.e. what method to call or property to push). The UCLP has the model pushing a change notification into a generic listener in the view, and then the view pulls the change information from the model; the module (and the controller, for that matter) do not know anything about the view besides a reference to the view’s notification listener.

Matching By Value vs. Matching By Reference

As shown in the sample application, one can use a more robust test to see if a Thingie is in the selected list than simply checking getIndexOf(). While the “thingie” might be a complex Value Object, this pattern also lends itself to simple data objects that might be dynamically created to wrap one or two column values. In this case, the object itself might not come from a global (i.e. singleton) store; one might have several simple instances that represent the same values, but not the same instances in memory.

For example, in the sample application (which has View Source)

 

Sample Message Based Checkbox List Application
click for SampleMessageBasedCheckboxListApplication

 

the AmISelected method checks if the data is selected, not just if the Thingie instance in the event is also on the list. Actually, the original version had a middle section that used api.business.gov to show a grid that was not even Thingie objects, but it has no crossdomain.xml, so…

Use this pattern to support your needs: if you have different representations of the same concept and you want to select or de-select the concept, then make the AmISelected check if the concept is on the list; if the objects in question are references to singleton objects on a central list (e.g. value objects), then check if the object itself is referenced by the list.

Let Me Know

Let me know what you think of this pattern. Let me know how I could explain it better. How have you improved it ?

Multiple States and Multiple Dependencies Using Message-Based Checkbox Patterns

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

While a basic Ultimate Checkbox List Pattern uses only a single Boolean value – e.g. isSelected – its questioning event can allow more than just one answer: in addition to finding out if a row is selected (e.g. checking a checkbox), a questioning event can determine if a specific list item is visible or enabled at all. For example, the current user might not have rights to see a particular value; the page might have a terse or verbose option; some criteria might be selectable based on a separate criteria selection; or a set of items might have a maximum number of selections.

Like the selected state, the enabled state of a list item could be embedded in the item data itself, however one can choose to extend the Ultimate Checkbox List Pattern to. As each change in the list (or its proxy) makes every item check its state (see Multiple Views Using a Message-Based Checkbox Pattern), disabling any or all items is as easy as disabling one (remember that only visible items have item renderers with listeners which check state).

public class ThingieEvent extends Event
{
    public function ThingieEvent(...)
    {
        ...
    }
    public var thingie : Thingie;
    public var isSelected : Boolean = false;
    public var isEnableded : Boolean = true; // new flag public var notificationListener : Function;
}

The item renderer registers a listener the first time it checks the current item’s state by adding it to the questioning event:

...
        var theEvent : ThingieEvent = new ThingieEvent(IS_THINGIE_SELECTED, true);
        theEvent.thingie = data as Thingie;
        theEvent.notificationListener = this.onExternalThingieChange;
        this.dispatchEvent(theEvent);
        isThingieSelectedCheck.selected = theEvent.isSelected;
        isThingieSelectedCheck.enabled = theEvent.isEnabled;
...

And whenever the item renderer’s notification listener indicates something external changed, it will check the state of the specific data currently in the item renderer.

    private function onExternalThingieChange(event : Event) : void
    {
        var theEvent : ThingieEvent = new ThingieEvent(IS_THINGIE_SELECTED, true);
        theEvent.thingie = data as Thingie;
        this.dispatchEvent(theEvent);
        isThingieSelecctedCheck.selected = theEvent.isSelected;
        isThingieSelecctedCheck.enabled = theEvent.isEnabled;
    }

On the list manager side, the first time the item renderer asks for a selection status by dispatching a questioning event, the list manager adds the notification listener (if present) using weak references. It can add the callback as a listener directly to the data list (e.g. ArrayCollection), or it can add it to a separate IEventDispatcher object in order to proxy events explicitly from multiple sources (e.g. signal a change when the _thingieList changes and when the user changes between “terse” to “verbose”).

    private var _thingieList : ArrayCollection = new ArrayCollection();
    private var _thingieListProxy : EventDispatcher = new EventDispatcher();
      ...
        _thingieList.addEventListener(CollectionEvent.COLLECTION_CHANGE, onThingiesChange);
        context.addEventListener(Event.STATE_CHANGE, onThingiesChange); // something else that requires a change-check
      ...

    private function onThingiesChange(event : Event = null) : void
    {
        _thingieListProxy.dispatchEvent(new Event(Event.CHANGE));
    }

    private function onIsThingieSelected(event : ThingieEvent) : void
    {
        event.isSelected = (_thingieList.getIndexOf(event.thingie) != -1);
        event.isEnabled = (context.currentState == SELECTABLE_STATE) && (_thingieList.length < MAX_SELECTED_THINGIE_COUNT);
        if (event.notificationListener != null)
            _thingieListProxy.addEventListener(Event.CHANGE, notificationListener, false, 0, true);
    }

If the onThingsChange event carried information, it would link the listener and the dispatcher more tightly than we want. Using a generic Event with the type Event.CHANGE (the simplest possible notification) insures that the dispatcher can send that notification for any reason it wants; it is not restricted by any required or expected event parameters.

For those who like sequence diagrams:sequence diagram for multiple=

(Update: sample application in the final installment)

Multiple Views Using a Message-Based Checkbox Pattern

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

The previous post showed how one can have an item renderer communicate with one of its containers to set and retrieve the selected (e.g. checked) state of the item renderer’s data. If, however, the same data (and its selected state) appear in more than one view, one needs to add another set of notifications to the pattern.

If something other than a particular item renderer changes the values in the thingieList, the item renderer needs to know to change the checkbox status. Rather than iterating through the children of the DataGrid or List to find all the ItemRenderers to tweak them, we can have sort of a “push” notification: the item renderer passes a listener function to the list manager the first time it asks for an item’s selected status, and external changes call that listener. To make things as generic as possible, the listener does not expect any specific type of event (i.e. the event does not contain information about the change); it simply checks its own status whenever it’s called.

This combination seems complicated, but it’s built on what we’ve done before:

  • The view dispatches an AmISelected event with a reference to its event listener function in it.
  • The list controller adds the event listener
  1. Something changes the list
  2. The list dispatches a CHANGE (or COLLECTION_CHANGE) event to all the listener functions
  3. The view listener functions dispatch an AmISelected event (without a listener reference in it)
  4. The list controller received the AmISelected event object and sets its .isSelected property based on the list
  5. The item renderer checks the .isSelected property and updates its display based on the answer


This causes a lot of events that don’t actually find any changes (i.e. every item renderer whose data have not be selected or deselected), however the cost for these events is low and the value of simplicity is high.

The item renderer registers a listener the first time it checks its data’s status:

 ...
        var theEvent : ThingieEvent = new ThingieEvent(IS_THINGIE_SELECTED, true);
        theEvent.thingie = data.thingie;

        theEvent.notificationListener = this.onExternalThingieChange; this.dispatchEvent(theEvent); // ---- listeners execute here

        isThingieSelectedCheck.selected = theEvent.isSelected;
 ...

And the item renderer’s listener function checks the status

private function onExternalThingieChange(event : Event) : void
{
    var theEvent : ThingieEvent = new ThingieEvent(IS_THINGIE_SELECTED, true);
    theEvent.thingie = data.thingie;
    this.dispatchEvent(theEvent);
    isThingieSelectedCheck.selected = theEvent.isSelected;
}

On the list manager side, it adds the callback function (if present) as a listener using weak references. It could add the callback as a listener directly to the data list or to a separate IEventDispatcher object if it was going to fire events explicitly

private function onIsThingieSelected(event : ThingieEvent) : void
{
    event.isSelected = (_thingieList.getIndexOf(event.thingie) != -1);

    if (event.notificationListener != null)
        _thingieList.addEventListener(CollectionEvent.COLLECTION_CHANGE, event.notificationListener, false, 0, true);
}

By adding one notification, changes to the selection list automatically update all items whether they are visible or become visible later, so the user can can modify or clear the list without tight coupling with all the other views that add and remove items.

(Update: sample application in the final installment)

The Ultimate Checkbox List Pattern

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

A common task is to put checkboxes into a list or grid. Instead of adding Boolean property values to the dataProvider items, the Ultimate Checkbox List Pattern leaves the display item alone (e.g. a pure value object) and uses membership in a separate list of items to indicate if an item is selected (checked). Often, this list of selected rows itself is very useful. Standard ActionScript events communicate between the item renderers (with a checkbox) and a container object or the form itself acting as a controller for the selected list.

The key to this pattern is anonymous communication: the list controller does not know what components are sending messages asking if this or that object is or is not selected, and the view (i.e. an ItemRenderer) does not know who is listening and answering its questions. They communicate using a custom event that adds a reference to the item, and a Boolean: isSelected. For example,

public class ThingieEvent extends Event
{
    public var thingie : Thingie;
    public var isSelected : Boolean = false;
    public var notificationListener : Function;
}

Changing the Selected State
The ItemRenderer sends an event when user interaction with its checkbox changes the selected state:

 ...
        var theEvent : ThingieEvent = new ThingieEvent(THINGIE_SELECTION, true);
        theEvent.thingie = this.data.thingie;
        theEvent.isSelected = thingieCheckBox.selected;
        this.dispatchEvent(theEvent);
 ...

The list manager might handle the event like this:

    private function onIsThingieSelected(event : ThingieEvent) : void
    {
        var existingIndex: int = _thingieList.getIndexOf(event.thingie);
        if (event.isSelected != (existingIndex != -1))
        {
            if (event.isSelected)
                _thingieList.addItem(event.thingie);
            else
                _thingieList.removeItemAt(existingIndex);
        }
    } 

    private var _thingieList : ArrayCollection = new ArrayCollection();

Getting the Selected State
When an ItemRenderer initializes itself, it needs to know if it should set the checkbox.selected attribute based on the data. It needs to ask someone outside itself if the current item should be checked. Rather than have the list manager add event listeners to every ItemRenderer, the ItemRenderer dispatches an event, waits for all the listeners to review it , and look at the event’s properties as the answer to the question.
It dispatches an event that bubbles (or is proxied up to the DataGrid or List) to some container acting as a list manager. It will set the event.isSelected property. Once the dispatchEvent() method returns, the ItemRenderer looks at that property.

 ...
    var theEvent : ThingieEvent = new ThingieEvent(IS_THINGIE_SELECTED, true);
    theEvent.thingie = data.thingie;

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

    isThingieSelectedCheck.selected = theEvent.isSelected;
 ...

Note that after the .dispatchEvent(), the event has whatever value the listeners put in there. On the list manager side, the listener might look like this:

    private function onIsThingieSelected(event : ThingieEvent) : void
    {
        event.isSelected = (_thingieList.getIndexOf(event.thingie) != -1);
    }

Note that it does not cost anything to add a listener more than once, and using a weak reference prevents memory issues. The Ultimate Checkbox List Pattern uses bubbling events; if one is not fond of them, one can proxy the events through custom Column classes, then through the top-level Grid or Listbox, and then up to whatever is acting as the list manager.

An advantage to bubbling events is immunity to changes in the number of containers between the item renderer and the list manager. One can even encapsulate some set of containers into a component and embed it in another container without changing this pattern.

The Ultimate Checkbox List Pattern has several advantages over using a flag in the dataProvider item to persist checked status:

  • The dataProvider items can remain pure value object (e.g. unchanged from an loosly-linked data module)
  • The same item can have separate selection-states on an infinite number of lists
  • The list of selected items is always current and available at a high level; one does not need to iterate through the dataProvider items
  • Items remain selected even when they are not visible or if they are not in the dataProvider’s collection. One can page off and back and previously selected items are still selected.
  • Items can be selected and deselected across several different views (if the list manager tests membership using an ID instead  of object identity). E.g. one can have browse lists and search results that reflect each other’s selection (ee the next posting for details).

(Update: sample application in the final installment)