Accessibility

Table of Contents

ActionScript collections and functional programming

ArrayCollection

It turns out that even if you use a raw Array as a dataProvider, that Array is automatically wrapped in an ArrayCollection:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application name="ArrayCollectionWrapping" xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:test="com.mindviewinc.test.*">
<test:People id="people" quantity="40"/>
<test:PeopleGrid id="dg1" dataProvider="{people.array}"/>
<mx:creationComplete>
import mx.collections.ArrayCollection
import mx.controls.Alert
Alert.show(String(dg1.dataProvider is ArrayCollection))
</mx:creationComplete>
</mx:Application>

When you run this, you'll get an alert that says "true."

If you have data that changes, the raw Array object doesn't generate change notifications to the data provider component, so that component doesn't get updated until it must be redrawn or if the data provider is reassigned. The ArrayCollection, however, generates these notifications. Thus, you should use an ArrayCollection to explicitly wrap a raw Array:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application name="ArrayCollectionNotification"
    xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:test="com.mindviewinc.test.*">
<test:People id="people" quantity="18"/>
<mx:ArrayCollection id="ac1" source="{people.array}"/>
<mx:ArrayCollection id="ac2" source="{people.array}"/>
<test:PeopleGrid id="dg1" dataProvider="{ac1}"/>
<test:PeopleGrid id="dg2" dataProvider="{ac2}"/>
<mx:Button label="delete last item"
    click="people.array.pop(); ac1.refresh(); ac2.refresh()"/>
<mx:Button label="delete last item (2)"
    click="people.array.pop();dg1.invalidateList();dg2.invalidateList()"/>
</mx:Application>

Notice the different approaches taken by each button: the first refreshes the ArrayCollection, the second invalidates the list in the PeopleGrid. In both cases the data on the PeopleGrid is updated, but the refresh causes a "push" from the ArrayCollection to the PeopleGrid.

We'll often want our arrays of Person objects to be contained within ArrayCollections, so here's a component that encapsulates the process:

package com.mindviewinc.test {
    import mx.collections.ArrayCollection
    import mx.core.UIComponent
 
    public class PeopleAC extends UIComponent {
        public var quantity:Number
        [Bindable] public var collection:ArrayCollection
        private var array:Array
        protected override function commitProperties():void {
            super.commitProperties()
            array = Person.createArray(quantity)
            collection = new ArrayCollection(array)
        }
    }
}

ArrayCollection operations

This example shows many of the operations available for the ArrayCollection, but not all -- the remainder you can find in the Flex Builder documentation or online. A DataGrid of Person objects serves to illuminate these operations:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application name="ArrayCollectionOperations"
    xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:test="com.mindviewinc.test.*"
    xmlns:display="com.mindviewinc.display.*">
<mx:Script>
import com.mindviewinc.test.Person
import mx.collections.*
[Bindable]
private var people:ArrayCollection = new ArrayCollection(Person.createArray(40))
private function filter(item:Object):Boolean {
    return item.city == "Springfield"
}
private function makeSort():Sort {
    var mySort:Sort = new Sort()
    // Sort on the state first, last name second.
    mySort.fields = [new SortField("state",true), new SortField("last",true)]
    return mySort
}
</mx:Script>
<mx:HDividedBox width="100%" height="100%">
    <mx:VBox height="100%">
        <mx:Button label="Add Filter" click="people.filterFunction=filter; people.refresh()"/>
        <mx:Button label="Remove Filter" click="people.filterFunction=null; people.refresh()"/>
        <mx:Button label="Add Sort" click="people.sort=makeSort(); people.refresh()"/>
        <mx:Button label="Remove Sort" click="people.sort=null; people.refresh()"/>
        <mx:Button label="Append" click="people.addItem(Person.createPerson())"/>
        <mx:Button label="Remove Item" click="people.removeItemAt(0)"/>
        <mx:Button label="Remove All" click="people.removeAll()"/>
        <mx:Button label="Add At #3" click="people.addItemAt(Person.createPerson(), 3)"/>
        <mx:Button label="Set Item #3" click="people.setItemAt(Person.createPerson(), 3)"/>
        <mx:Button label="Get Item #3" click="t.text=String(people.getItemAt(3))"/>
        <display:TextDisplay id="t" width="100%" height="40"/>
    </mx:VBox>
    <test:PeopleGrid dataProvider="{people}"/>
</mx:HDividedBox>
</mx:Application>

Notice that when you apply or remove sorts and filters, you must call refresh() in order for the results to propagate to the display component, but all other operations automatically propagate.

Cursors (Iterators)

Although there's only a single method that pertains to cursors, createCursor() returns an IViewCursor that has some interesting properties and methods:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application name="ArrayCollectionCursors"
    xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:test="com.mindviewinc.test.*"
    xmlns:display="com.mindviewinc.display.*">
<mx:Script>
import com.mindviewinc.test.Person
import mx.collections.*
[Bindable]
private var people:ArrayCollection = new ArrayCollection(Person.createArray(20));
[Bindable]
private var cursor:IViewCursor
private var bookmark:CursorBookmark
private function update():void {
    if(!cursor) return
    t.text = String(cursor.current)
    before.text = "cursor.beforeFirst: " + cursor.beforeFirst
    after.text = "cursor.afterLast: " + cursor.afterLast
}
private function findBob():void {
    // findLast() and findAny() also require a sorted ArrayCollection.
    people.sort = new Sort()
    people.sort.fields = [new SortField("first",true)]
    people.refresh()
    cursor = people.createCursor()
    cursor.findFirst({first:"Bob"})
    update()
}
</mx:Script>
<mx:HDividedBox width="100%" height="100%">
    <mx:VBox height="100%">
        <mx:Button label="Get Cursor" click="cursor=people.createCursor();update()"/>
        <mx:Button label="Cursor Forward" click="if(cursor) cursor.moveNext();update()"/>
        <mx:Button label="Cursor Backward" click="if(cursor) cursor.movePrevious();update()"/>
        <mx:Button label="Remove" click="if(cursor) cursor.remove();update()"/>
        <mx:Button label="Insert" click="if(cursor) cursor.insert(Person.createPerson());update()"/>
        <mx:Button label="Find Bob" click="findBob()"/>
        <mx:Button label="Set Bookmark" click="if(cursor) bookmark=cursor.bookmark"/>
        <mx:Button label="Clear Sort" click="people.sort=null;people.refresh()"/>
        <mx:Button label="Goto Bookmark" click="if(cursor) cursor.seek(bookmark);update()"/>
        <display:TextDisplay id="t" width="100%" height="40"/>
        <mx:Label id="before" />
        <mx:Label id="after" />
    </mx:VBox>
    <test:PeopleGrid dataProvider="{people}"/>
</mx:HDividedBox>
</mx:Application>

In general, you can do a lot without cursors so you may not see them used that often. However, if you write code that uses cursors then it works with both ArrayCollection and XMLListCollection.