Silence is Foo Mental notes on Ruby, Git, Rails and whatever geeky thing

2Nov/10Off

[MentalNote] Hand cursor in Flex components

It's really impressive that we can do amazing things with Flex and that we can't show the hand cursor when the mouse is over a component in a straight forward and understandable way.

Every time I've needed to do that I've ended up very pissed off because I forget that the property useHandCursor=true doesn't show the hand cursor by itself in some components, I must set another properties in order for it to work properly, so here is my mental note to remember it.

For components as Image the only thing we need to do is:

<!-- MXML -->
<Image id="my_image" source="path_to/image.jpg" buttonMode="true" />

or

//AS3
my_image.buttonMode = true

but for instance, for the Label component and other ones you have to do this:

label.useHandCursor = true
label.buttonMode = true
label.mouseChildren = false

so, what I do is to use those three properties in all the components. If the component doesn't inherits from flash.display.Sprite then I add such component to a Sprite and that'd be it.

pretty easy, isn't it? :S

28Jun/10Off

Creating a common sorting function for a DataGrid control in Flex

THE PROBLEM

You have several non-related data grids (different columns with different field names) in the same application and you want to sort this columns of different data grids using the same sorting function.

THE SOLUTION

The first try would be using the DataGridColumn#sortCompareFunction method in the following way:

private function mySortingFunction ( obj1:Object, obj2:Object ) : int
{
     //compare <code>obj1.myField</code> against <code>obj2.myField</code> here

     //The function should return a value based on the comparison of the objects:

     //  -1 if obj1 should appear before obj2 in ascending order.
     //   0 if obj1 = obj2.
     //   1 if obj1 should appear after obj2 in ascending order.
}

Now you send the parameter to sortCompareFunction and this parameter must be a defined function

var myColumn:AdvancedDataGridColumn = new AdvancedDataGridColumn();

myColumn.sortCompareFunction = "mySortingFunction";

NOTE: when the user clicks the column header to sort the datagrid Flex internally will call the "mySortingFunction" and will send the proper parameters.

As you can see you have to *specify the field* you want to sort on (the field is hard-coded), what if you want to sort another column field with a different name? you wouldn't be able to do it, you'd have to repeat the same function.

The second try would be the next one:

public class Comparators
{
   public static const NUMERIC:String = "numeric";
   public static const DATE:String = "date";

   public static function sorterProxy(field:String, sortingType:String = NUMERIC):Function
   {
      return function ( obj1:Object, obj2:Object ) : int
      {
         if ( sortingType == NUMERIC )
         {
            return myNumericCompareFunction ( obj1[field], obj2[field] );
         }
         else if ( sortingType == DATE )
         {
          return myDateCompareFunction( obj1[field], obj2[field] );
         }
     }
  }

  private static function myNumericCompareFunction ( obj1:Object, obj2:Object ) : int {}
  private static function myDateCompareFunction ( obj1:Object, obj2:Object ) : int {}
}

As you can see, the sorterProxy method is returning a function (:Function) so we're ok with the sortCompareFunction method requirements.

c1.sortCompareFunction = Comparators.sorterProxy ( "field1", Comparators.NUMERIC );

c2.sortCompareFunction = Comparators.sorterProxy ( "field2", Comparators.DATE );

Ok, all this works, isn't it? I mean, you can add one static constant for every different kind of comparison you need and you'd be able to use the same sorting function in every data grid and every column you want. This would work for a few types of comparison, but if you need to use more than 5 all those IF statements would be very ugly.

Let's try a different approach: An interface and one class for every kind of comparison. This might be called the Strategy design pattern.

This is the contract (IComparator.as file) or the interface

public interface IComparator
{
    function compare ( obj1:Object, obj2:Object ) : int;
}

Comparison classes (in a "comparators" folder)

//DateComparator.as file
public class DateComparator implements IComparator
{
      public function compare ( obj1:Object, obj2:Object ) : int
      {
         //here you'd compare the data in the way you want
      }
}

//SpecialComparator.as file
public class SpecialComparator implements IComparator
{
      public function compare ( obj1:Object, obj2:Object ) : int
      {
         //here you'd compare the special data in the way you want
      }
}

This is the new signature of the sorterProxy method in the Comparators class

public static function sorterProxy(field:String, comparisonStrategy:IComparator):Function
{
      return function ( obj1:Object, obj2:Object ) : int
      {
         return comparisonStrategy.compare ( obj1[field], obj2[field] );
      }
}

Look at the comparisonStrategy type, it's IComparator, which means that all the classes that implements the IComparator interface will be a valid argument because we'd always call the "compare" method, and all the classes that implements such interface must define a method called "compare" that accepts two objects as arguments (integers, strings, etc) and returns an integer (-1, 0 or 1).

Now the final implementation

c1.sortCompareFunction = Comparators.sorterProxy ( "field1", new SpecialComparator() );

c2.sortCompareFunction = Comparators.sorterProxy ( "field2", new DateComparator() );

c3.sortCompareFunction = Comparators.sorterProxy ( "field3", new SpecialComparator() );

Enjoy!