Flex Array Collection Sort and Filtering

It’s not unusual to need to write an application that can filter and sort a list of items based on user input. Maybe your e-commerce shop offers a variety of notebooks from different companies, and you want visitors to be able to sort or filter your selection. Combining Flex with ActionScript allows you to create such an application with a minimum amount of hassle (and code), as you’ll see in this article.

If you’re already familiar with Flex and ActionScript, you will know how useful the listBase controls are to fast RIA development.  When working with the List, Tree, DataGrids and other controls, you may first be looking to get over the hump of populating your control with the correct data.  Understanding the breadth and depth of dataProviders is an article in itself (not discussed here).  

Once you do have an understanding of them, you may be in a situation where you will need to do further manipulations, like sorting or filtering.  The ActionScript library has a few nifty classes that work in conjunction with the ArrayCollection (used as a dataProvider for list-based controls) that help with this type of functionality.  In this article I will build a basic application which uses an ArrayCollection as a dataProvider and apply sorting and filtering to it.

Building Our Application

To start with, let’s talk about what we want to build.  We really need a list of items that lend themselves to being sorted and filtered.  For our data, we will use modern desktop and laptop computers.  Because the DataGrid already has some built-in sorting, we will just use a plain List control to demonstrate sorting and filtering.  I’ve purposely used the word Laptop, Netbook and Desktop for easy filtering. 


Example Data:


Dell Mini 9 Netbook

Dell Inspiron 546 Desktop

Dell Inspiron 14 Laptop

Alienware Aurora Desktop

Alienware M17 Laptop

HP Pavillon p6100 Desktop

Compaq Presario CQ60Z Laptop

Toshiba Teca M10 Laptop


For the UI, we will want to allow the user to enter text which will filter the list. So, we will use a listControl and a TextInput Control.  As the user types text in the TextInput, the list will remove items that don’t match.  Here is what our app looks like:


 

 


 


 

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

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

                layout="vertical" 

                width="100%" height="100%">

 


    <mx:HBox width="100%" height="25" verticalAlign="middle">

        <mx:Label text="Filter"/>

        <mx:TextInput id="search" width="100%"/> 

   </mx:HBox>

 


   <mx:List id="computers" width="100%" height="100%">

         <mx:ArrayCollection id="computersCollection">

             <mx:Object label="Dell Mini 9 Netbook"/>

                     <mx:Object label="Dell Inspiron 546 Desktop"/>

                     <mx:Object label="Dell Inspiron 14 Laptop"/>

                     <mx:Object label="Alienware Aurora Desktop"/>

                     <mx:Object label="Alienware M17 Laptop"/>

                     <mx:Object label="HP Pavillon p6100 Desktop"/>

                     <mx:Object label="Compaq Presario CQ60Z Laptop"/>

                     <mx:Object label="Toshiba Teca M10 Laptop"/>

         </mx:ArrayCollection>

   </mx:List>

 


</mx:Application>


You’ll notice that we used the "default property" to set the dataProvider of the List.  The List control could have also been written more explicitly as follows, where we specify the dataProvider.


   <mx:List id="computers" width="100%" height="100%">

     <mx:dataProvider>

         <mx:ArrayCollection id="computersCollection">

             <mx:Object label="Dell Mini 9 Netbook"/>

             <mx:Object label="Dell Inspiron 546 Desktop"/>

             <mx:Object label="Dell Inspiron 14 Laptop"/>

             <mx:Object label="Alienware Aurora Desktop"/>

             <mx:Object label="Alienware M17 Laptop"/>

             <mx:Object label="HP Pavillon p6100 Desktop"/>

             <mx:Object label="Compaq Presario CQ60Z Laptop"/>

             <mx:Object label="Toshiba Teca M10 Laptop"/>

         </mx:ArrayCollection>

      </mx:dataProvider>

   </mx:List>

The TextInput will be used by the user to filter the list of computers in the List.  Now that we have a working application, let’s start the process of filtering the data based on the user input.

{mospagebreak title=Implementing the Filter}

The process we will use to filter the list is quite simple.  Flex and ActionScript have made this job easy for us by providing the filterFunction attribute of the ArrayCollection.  This attribute points the ArrayCollection to a function that we will write.

The function determines which of the elements of the collection will be displayed and which elements will not be displayed. The function is called once for every item in the list. The function must has a Boolean return type which translates TRUE to display the object and FALSE to NOT display this object.

Here is what our filter function will look like: 

public function filter(item:Object):Boolean{

  

       var tmpString:String = new String(item);

          

      // If there is nothing is the search box

      if (search.text==""){

           return true;

      }

      

      // if the contents of the search box 

      // is a sub string of this item

      if (tmpString.indexOf(search.text)!=-1){

          return true;

      }

          

      return false;

   }


Let’s break this down. The item is passed to our function by the ArrayCollection as an Object. The first thing that our function does is create a new String object out of the item being tested for display. We do this because we want to make use of the some the string methods (indexOf).

Next, we check to see if our search TextInput is blank. If it is, then we should not filter anything. In this case, we simply return TRUE for all elements. If the search TextInput DOES have text in it, we would have to continue execution of this function in order to see if the contents of the search TextArea is a substring of the item being tested for display. If the search text turns out to be a substring of the item, we would return TRUE, thereby causing the item to be displayed.

At the start of the application, all items are display; however, if we were to type into our TextInput, nothing would change in our list. This is because we need to tell the the ArrayCollection to refresh itself when a character has been pressed in the search box. To do this, we add the following event to the search input: 


     <mx:TextInput id="search" width="100%"  keyUp="computersCollection.refresh()"/>


Refreshing the ArrayCollection effectively calls the filterFunction again — once per item.  

Here is a look at the completed application: 

In the example image above example, notice that only items with the word Laptop are displayed.

{mospagebreak title=Making it Case Insensitive}

If you were to type "laptop" instead of "Laptop" you would not see any results. This is because our function cares about the case of the letters. To fix this, we can simply change our filter function so that the comparison is case insensitive. Here is an updated portion of the filter function: 

      // if the content of the search box 

      // is a sub string of this item

      if (tmpString.toLowerCase().indexOf(search.text.toLowerCase())!=-1){

          return true;

      }

 


Notice the addition of the ‘toLowerCase‘ methods being call on tmpString and search.text.  This converts both strings to lowercase before testing to see if one is a substring of the other.

Sorting Our ArrayCollection

For the remaining portions of this article we talk about sorting our list in ascending and descending order. Much like the filterFunction, the ArrayCollection object provides an object for sorting. Although a little bit more complicated, much of the work is done for us thanks to encapsulation. To get our sorting algorithm in place we need to create two objects, the Sort and the SortField. Here is the code:

 // version 1

 var sort:Sort = new Sort(); 

 sort.fields = [new SortField("label", true)];


The first line is a general Sort object which our ArrayCollection will reference. The sort object has a property called fields which references an array of the SortField Object. This sounds more complicated than it really is. Here is another way to write the same code that is easier to read:

// version 2

// create a new sort object

var sort:Sort = new Sort(); 


// Create a sort field.

// The sort field is a label name in the 

// ArrayCollection.  The sorting will be

// performed on this field.  The second argument

// determines if the sort is case insensitive

var sortArray:Array = new Array();

var sortField = new SortField("label",true);


// Add the sort field to the array

sortArray.push(sortField);


// assign sort fields to the sort object

sort.fields = sortArray;


// assign the sort object to our array collection

computersCollection.sort = sort;

This alternative method is drawn out a bit more and does not take advantage of the short form of creating Arrays. Nonetheless, both ways are valid. For the remainder of the article we will go with version 1 of the sorting code. We need to place this code in our application.

Also note that you will have to import the sort classes, as in:

import mx.collections.Sort;

import mx.collections.SortField;


{mospagebreak title=Adding the Sort Button}

Our sort code above successfully connects the Sort object to our ArrayCollection. This code needs to be executed once before we can make greater use of it. To make this possible, we will create a new function that runs after the application has been created.  We will take advantage of the creationComplete event that is triggered once the application is created. We will modify the Application Tag in the following way: 

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

                layout="vertical" 

                width="100%" 

                height="100%" 

                creationComplete="init()">


Notice the newly-added creationComplete="init()". This will cause the init function to be called when the application is created. Here is the init function along with some Sort Object declaration (in public scope):

   import mx.collections.Sort;

   import mx.collections.SortField;


   // create our sort object    

   public var sort:Sort = new Sort();

   

   // connect the sort object to the ArrayCollection

   public function init():void{

       sort.fields = [new SortField("label", true)];

       computersCollection.sort=sort;

   }


This code will not sort the list immediately because the creationComplete event is triggered after the controls on the screen have been displayed. What you will see is the natural order as listed in our dataProvider. To cause the list to be sorted we will add a sort Button to our UI. We’ll add this code to our MXML after the <mx:List> tag: 

<mx:HBox>

    <mx:Button label="Toggle Sort Order" click="toggleOrder(event)"/>

</mx:HBox>


Our button has a click event which refers to the following function: 

public function toggleOrder(event:MouseEvent):void{  

   sort.reverse();

   computersCollection.refresh();

}

 


The toggleOrder function calls the sort object’s reverse property. Next, we refresh the ArrayCollection so that it will re-apply the sorting algorithm. That is all that is needed to switch the sort order from ascending to descending order.

So there you have it. We’ve successfully created a Flex application which can filter a list of items based on user input as well as sort them when the user initiates it. Flex/ActionScript provides easy to use methods that allow this type of processing to be performed with a minimum amount of code. Our Flex application was a mere 73 lines of code, which included the list items (they would normally be pulled from an external source).

You can see the full Flex app running at: http://www.reefkeysoftware.com/blog/sort_filter

Resources

http://livedocs.adobe.com/flex/3/langref/mx/collections/ArrayCollection.html

http://livedocs.adobe.com/flex/3/langref/mx/collections/Sort.html

http://livedocs.adobe.com/flex/3/langref/mx/collections/SortField.html

http://livedocs.adobe.com/flex/3/html/help.html?content=mxmlSyntax_3.html

http://livedocs.adobe.com/flex/3/langref/String.html

http://livedocs.adobe.com/flex/3/langref/mx/core/Application.html#eventSummary
 

[gp-comments width="770" linklove="off" ]

chat