What is an ArrayCollection?

Posted: 06/04/09

An image of What is an ArrayCollection?

What is an ArrayCollection?

For developers new to Flex, possibly one of the most confusing parts of understanding how to use the Flex Framework is the use of Collections. Collections underpin a lot of the core data display controls in Flex (namely DataProviders), and understanding how they work not only allows effective use of these controls, but also gives the developer a more robust way of handling data throughout their application.

An ArrayCollection is essentially an Array, with a wrapper around it that provides the developer with an enhanced interface to add, remove and sort data. The wrapper also allows notification of changes to the data inside ArrayCollection to be send to registered listening objects as events. If you imagine an ArrayCollection as an Array wearing the kind of robot suit Robert Downey Jnr wore in Ironman, you should get on fine... It does a few extra impressive things, but ultimately there’s an Array inside.

ArrayCollection extends ListCollectionView, which is an implementation of ICollectionView - a view onto a collection of data.

Declaring an ArrayCollection:

An ArrayCollection can be declared in a Flex application in two main ways. With an MXML document it is possible to declare your ArrayCollection with MXML mark-up, or with ActionScript in your script block. In an ActionScript class, it is only possible to declare your ArrayCollection using ActionScript.

With MXML:

<mx:ArrayCollection id="myAC">

<mx:Array>

<mx:Object name="Jodie" country="UK" language="English"/>

<mx:Object name="Kate" country="USA" language="English"/>

<mx:Object name="David" country="France" language="French"/>

</mx:Array>

</mx:ArrayCollection>

The exact same declared with ActionScript:

import mx.collections.ArrayCollection;

private var myAC:ArrayCollection = new ArrayCollection( [ { name: "Jodie", country: "UK", language: "English" }, { name: "Kate", country: "USA", language: "English" }, { name: "David", country: "France", language: "French" } ] );

It is also possible to declare and instantiate your ArrayCollection, and then assign an Array to it, or switch between Arrays at runtime using the source property:

var myArray:Array = [ { name: "Jodie", country: "UK", language: "English" }, { name: "Kate", country: "USA", language: "English" }, { name: "David", country: "France", language: "French" } ];

MyAC.source = myArray;

Adding and removing items from the ArrayCollection

The enhanced interface that the ArrayCollection provides allows you to easily add and remove objects from the underlying Array, including adding and removing objects at a specific index

myAC.addItem( { name: "Lee", country: "China", language: "Mandarin" } );

myAC.removeItemAt( 0 );

myAC.addItemAt( { name: "Jodie", country: "UK", language: "English" }, 0 );

The first line adds an item to the end of the underlying Array. The second line removes the first item in the underlying Array; causing all of the remaining items to move down one position. Finally, the third line adds a new item to the first position in the Array; causing all of the other items to move up one position.

Sorting an ArrayCollection

The data in the ArrayCollection is now very mixed-up, with little or no order to it, since we’ve added and removed some items.

Being able to sort data in the ArrayCollection with relative ease is something that is provided with the combined use of Sort() and SortField() objects.

var mySort:Sort = new Sort();

mySort.fields = [ new SortField( "country", true ) ];

myAC.sort = mySort;

myAC.refresh();

trace( myAC );

The important thing to always remember (but you’ll forget at least once) is to ensure you call the refresh() method on the ArrayCollection after applying the sort. Failing to do so will not update the ArrayCollection.

The handy thing about specifying your sort fields as an Array it that you can add more, offering a first, second, third (or more) order of sorting. In this example, the resulting ArrayCollection will be sorted by country first, followed then by name, and then by language:

var mySort:Sort = new Sort();

mySort.fields = [ new SortField( "country", true ), new SortField( "name", true ), new SortField( "language", true ) ];

myAC.sort = mySort;

myAC.refresh();

Receiving notifications when things change

Probably the most useful aspect of an ArrayCollection over an Array is its ability to notify when changes have been made to it. Because objects like ArrayCollection dispatch the CollectionEvent.COLLECTION_CHANGE event, they’re not only able to be used with the manual event dispatch and listening procedures that most Flash Developers are familiar with, but they also plug-in to the Binding capabilities of the Flex Framework. This means that through the use of very simple meta-data and curly-brace notation {}, the listening and handling of events can be handled entirely by the Binding (more about Data Binding in Flex). As a developer, this means that you can remove some of the hassle of understanding when you need to update a display object with updated data.

In the first example, here’s how manual notification of changes to the data in the underlying Array of an ArrayCollection works:

myAC.addEventListener( CollectionEvent.COLLECTION_CHANGE, onChange );

private function onChange( e:CollectionEvent ) :void

{

trace( 'collection changed' );

}

Note that sorting an ArrayCollection will dispatch the COLLECTION_CHANGE event as well as when you add and remove items; this is because a sort changes the position of items in the underlying Array.

Manually assigning listeners and responding to changes in the ArrayCollection is something you’ll seldom do when building Flex applications, this is because Bindings generally handle most of this for you. In the next example, a DataGrid is updated each time the ArrayCollection changes. The ArrayCollection is defined using MXML mark-up. Feel free to paste this code into your IDE and run it.

<?xml version="1.0" encoding="utf-8"?>

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

<mx:Script>

<![CDATA[

import mx.events.CollectionEvent;

import mx.collections.SortField;

import mx.collections.Sort;

private function onClick( e:MouseEvent ) :void

{

myAC.addItem( { name: "Lee", country: "China", language: "Mandarin" } );

myAC.removeItemAt( 0 );

myAC.addItemAt( { name: "Jodie", country: "UK", language: "English" }, 0 );

sortCollection();

}

private function sortCollection() : void

{

var mySort:Sort = new Sort();

mySort.fields = [ new SortField( "country", true ), new SortField( "name", true ), new SortField( "language", true ) ];

myAC.sort = mySort;

myAC.refresh();

}

]]>

</mx:Script>

<mx:ArrayCollection id="myAC">

<mx:Array>

<mx:Object name="Jodie" country="UK" language="English"/>

<mx:Object name="Kate" country="USA" language="English"/>

<mx:Object name="David" country="France" language="French"/>

</mx:Array>

</mx:ArrayCollection>

<mx:DataGrid id="myDG" dataProvider="{myAC}"/>

<mx:Button id="myButton" label="Update" click="onClick(event)"/>

</mx:Application>

As you’ll see, clicking the button labelled “Update” causes the ArrayCollection have an item removed, and two items added, and finally a sort applied to it. By specifying the dataProvder property of the DataGrid as ‘bound’ to the ArrayCollection instance, through the use of curly-braces {}, the binding capabilities of the Flex Framework take over when the ArrayCollection changes, and automatically notify the DataGrid to update.

Searching an ArrayCollection for a value

Another important thing to be able to is search your ArrayCollection for occurrences of a particular value. This can be done with the use of a Cursor object, that acts like a marker on your ArrayCollection, highlighting where a match is found. The key thing to remember with a Cursor is that it needs to be used in conjunction with a Sort; if you haven’t yet applied a sort to the ArrayCollection, the Cursor isn’t going to work.

In the code below, I apply a Sort to the ArrayCollection when the application has completed rendering. The user is then able to enter a search term into TextInput field, and when they click on the search button, a cursor is created that performs a look-up along the “name” values in the objects on the underlying Array for instances of the value entered. If one is found, the Cursor position is updated, and the resulting event callback updates another ArrayCollection that is bound to a DataGrid; showing the result that was found.

Again, copy the code into your IDE and run it, if you like.

<?xml version="1.0" encoding="utf-8"?>

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

<mx:Script>

<![CDATA[

import mx.events.FlexEvent;

import mx.collections.IViewCursor;

import mx.events.CollectionEvent;

import mx.collections.SortField;

import mx.collections.Sort;

import mx.collections.ArrayCollection;

private function init( e:Event ) :void

{

var mySort:Sort = new Sort();

mySort.fields = [ new SortField( "name", true ), new SortField( "language", true ), new SortField( "country", true ) ];

myAC.sort = mySort;

myAC.refresh();

}

private function onSearchClick( e:MouseEvent ) :void

{

var cursor:IViewCursor = myAC.createCursor();

cursor.addEventListener( FlexEvent.CURSOR_UPDATE, onCursorUpdate );

cursor.findAny( { name:null,language:null,country:searchText.text } );

}

private function onCursorUpdate( e:FlexEvent ) :void

{

myCursorResult.source = [ {name: e.target.current.name, country:e.target.current.country} ];

}

]]>

</mx:Script>

<mx:ArrayCollection id="myAC">

<mx:Array>

<mx:Object name="Jodie" country="UK" language="English"/>

<mx:Object name="Kate" country="USA" language="English"/>

<mx:Object name="David" country="France" language="French"/>

</mx:Array>

</mx:ArrayCollection>

<mx:ArrayCollection id="myCursorResult"/>

<mx:TextInput id="searchText"/>

<mx:Button label="Search" click="onSearchClick(event)"/>

<mx:DataGrid dataProvider="{myCursorResult}"/>

</mx:Application>

You also need to bear in mind that the search properties specified in the object you supply to the Cursor’s findAny(), findFirst() or findLast() methods need to be in the same order that they are specified in the Sort. For example, in the above code, my “name” SortField comes first, so I can specify the name field in the Cursor. If, however, I wanted to search for “country”, I would need to specify “name”, “language” and the “country” in my Cursor, as this is the order by which they are specified in the sort.

You see that by changing the sort applied to the ArrayCollection, so that only the “country” sort field is specified, I don’t need to provide the other field values to the Cursor, and the search will only include matching “country” objects.

private function onSearchClick( e:MouseEvent ) :void

{

var mySort:Sort = new Sort();

mySort.fields = [ new SortField( "country", true ) ];

myAC.sort = mySort;

myAC.refresh();

var cursor:IViewCursor = myAC.createCursor();

cursor.addEventListener( FlexEvent.CURSOR_UPDATE, onCursorUpdate );

cursor.findAny( { country:searchText.text } );

}

So there you have it; more than a seemingly useless object that Adobe insists you convert your objects to so as to be able to use the List components! It does plenty more than I’ve shown here, and hopefully I’ll be able to follow this post up with some more interesting uses of ArrayCollection – provided this post is well received! Remember also that there’s XMLListCollection, which brings E4X to the party too...

Further reading:

Implementing the Presentation Model in Flex using daisy-chained ArrayCollections

Keywords for this post: ArrayCollection, array, collection, ICursorView, Cursor, List, Sort, Search, Event, Binding, actionscript 3, flex