Wednesday, July 6, 2011

ArrayCollection in Flex: Part 5 - Searching using a Cursor

Today we will learn how to search an ArrayCollection for a value using a Cursor object.

A Cursor acts like a marker on your ArrayCollection, highlighting the part where a match of the search is found. A Cursor can be created using the IViewCursor class. If you think of ArrayCollection elements as a row of objects, the cursor is a marker that can go forwards and backwards in the row, pointing to one object at a time. To use a cursor with an ArrayCollection, we need to sort the collection first.

Using a cursor, we can search for values among the elements of an ArrayCollection, using the findAny(), findFirst() or findLast() methods. We can use the remove() method of the cursor object to remove the highlighted element from the ArrayCollection.

First, let's create an application that lets us search for values in an ArrayCollection. We will need 2 DataGrids - one for displaying the content of the ArrayCollection, and the second one for displaying search results. We'll need to include a text input field and a button for searching too.

Here's what the application will look like:


The first thing we'll need to do is update our Declarations to add a new ArrayCollection that we will call resultAC.

<fx:Declarations>
<mx:ArrayCollection id="myAC">
<fx:Object firstname="John" lastname="Jackson" age="25" />
<fx:Object firstname="Bob" lastname="Thompson" age="30" />
<fx:Object firstname="Andy" lastname="Doyle" age="30" />
</mx:ArrayCollection>

<mx:ArrayCollection id="resultAC" />
</fx:Declarations>

Next, we set up the visual elements. We add a text input with an id of myInput, a button, that calls a doSearch() function on click and a DataGrid object that is data bound to resultAC collection.

<s:VGroup>
<mx:DataGrid dataProvider="{myAC}" sortableColumns="false" width="300" />
<s:HGroup>
<s:TextInput id="myInput" /><s:Button label="Search name" click="doSearch();" />
</s:HGroup>
<s:Label text="Search results:" />
<mx:DataGrid dataProvider="{resultAC}" sortableColumns="false" width="300" height="70" />
</s:VGroup>

In the Application tags, add an attribute creationComplete and set it to an init() function. It will be called when the application is created.

creationComplete="init()"

In the init function we need to create sorting for the myAC collection, because, as I said before, the collection needs to be sorted before we can use a cursor on it.

private function init():void {
var mySort:Sort = new Sort(); 
mySort.fields = [ new SortField( "firstname", true ) ];
myAC.sort = mySort;
myAC.refresh();
}

Now, the doSearch() function. Here we create a new IViewCursor object and set it as myAC's cursor. We give it an event listener, which listents for a CURSOR_UPDATE event. We then use the findAny() method to find an item which has the attribute values that are specified in the parameter. Also we will set the resultAC.source to a blank array, to refresh it.

private function doSearch():void {
resultAC.source = [];
var cursor:IViewCursor = myAC.createCursor();
cursor.addEventListener( FlexEvent.CURSOR_UPDATE, cursorUpdate );
cursor.findAny( { firstname:myInput.text } );
}

Because we are only searching by first names, we can only include that attribute, but we can also include lastname and age attributes and set them to null:

cursor.findAny( { firstname:myInput.text, lastname:null, age:null} );

The cursorUpdate function will take the myAC item that it is now pointing to (the one that matched the search criteria), takes its data and creates a new array with this one object, which it then passes as the source of resultAC collection:

private function cursorUpdate(evt:FlexEvent):void {
resultAC.source = [ { firstname: evt.target.current.firstname, lastname: evt.target.current.lastname, age: evt.target.current.age } ];
}

And there we go. The full code is:

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
               xmlns:s="library://ns.adobe.com/flex/spark"
               xmlns:mx="library://ns.adobe.com/flex/mx"
   width="320" height="350"
   creationComplete="init()">
   
<fx:Declarations>
<mx:ArrayCollection id="myAC">
<fx:Object firstname="John" lastname="Jackson" age="25" />
<fx:Object firstname="Bob" lastname="Thompson" age="30" />
<fx:Object firstname="Andy" lastname="Doyle" age="30" />
</mx:ArrayCollection>

<mx:ArrayCollection id="resultAC" />
</fx:Declarations>


<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.collections.Sort;
import mx.collections.SortField;
import mx.events.FlexEvent;
import mx.collections.IViewCursor;

private function init():void {
var mySort:Sort = new Sort(); 
mySort.fields = [ new SortField( "firstname", true ) ];
myAC.sort = mySort;
myAC.refresh();
}

private function doSearch():void {
resultAC.source = [];
var cursor:IViewCursor = myAC.createCursor();
cursor.addEventListener( FlexEvent.CURSOR_UPDATE, cursorUpdate );
cursor.findAny( { firstname:myInput.text } );
}

private function cursorUpdate(evt:FlexEvent):void {
resultAC.source = [ { firstname: evt.target.current.firstname, lastname: evt.target.current.lastname, age: evt.target.current.age } ];
}
]]>
</fx:Script>

<s:VGroup>
<mx:DataGrid dataProvider="{myAC}" sortableColumns="false" width="300" />
<s:HGroup>
<s:TextInput id="myInput" /><s:Button label="Search name" click="doSearch();" />
</s:HGroup>
<s:Label text="Search results:" />
<mx:DataGrid dataProvider="{resultAC}" sortableColumns="false" width="300" height="70" />
</s:VGroup>

</s:Application>

Remember that the search is performed with the use of a cursor, and because the cursor can only highlight one item at a time, this will always show only one item in the results. This means that this method is not really good for searching ArrayCollections. What's the point of all this, then? Well, now we know how to use this kind of search, and we can now use it for some actions that we might need to do to the ArrayCollection.

For example, it is often needed to remove a specific item in a collection. To let the user do that, we can see which item he selects and tries to remove in whatever user interface, then create an IViewCursor object and highlight this item to remove it using the cursor. Removing an item that is selected by the cursor is very easy - we just call the remove() method of the cursor object.

Here's an example where you can search for a name and remove a person who has that name:


The code to that example:

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
               xmlns:s="library://ns.adobe.com/flex/spark"
               xmlns:mx="library://ns.adobe.com/flex/mx"
   width="320" height="235"
   creationComplete="init()">
   
<fx:Declarations>
<mx:ArrayCollection id="myAC">
<fx:Object firstname="John" lastname="Jackson" age="25" />
<fx:Object firstname="Bob" lastname="Thompson" age="30" />
<fx:Object firstname="Andy" lastname="Doyle" age="30" />
</mx:ArrayCollection>
</fx:Declarations>


<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.collections.Sort;
import mx.collections.SortField;
import mx.collections.IViewCursor;

private function init():void {
var mySort:Sort = new Sort(); 
mySort.fields = [ new SortField( "firstname", true ) ];
myAC.sort = mySort;
myAC.refresh();
}

private function doSearch():void {
var cursor:IViewCursor = myAC.createCursor();
if(cursor.findFirst( { firstname:myInput.text, lastname:null, age:null } )){
cursor.remove();
}
}
]]>
</fx:Script>

<s:VGroup>
<mx:DataGrid dataProvider="{myAC}" sortableColumns="false" width="300" />
<s:TextInput id="myInput" text="Enter name here" />
<s:Button label="Search by first name and remove" click="doSearch();" />
</s:VGroup>

</s:Application>

There are many uses for a cursor in a collection. It can be used to simply navigate through the collection using moveNext() and movePrevious() methods and display only one item at a time, as well as add items to a specific place in the collection using the insert() method.

That's all for today.

Thanks for reading!

Related:

ArrayCollection in Flex: Part 1 - Declaration
ArrayCollection in Flex: Part 2 - Sorting
ArrayCollection in Flex: Part 3 - Listening to updates
ArrayCollection in Flex: Part 4 - Filtering the elements

No comments:

Post a Comment