⇥ Formatting Flex Datagrid rows

I keep coming across people who seem to think that Flex’s data formatting facilities are somewhere between Voodoo and rocket surgery, particularly when it comes to data visualization elements like DataGrids.

Nothing could be further from the truth—Flex may not be the best system in the world, but it’s all about manipulating data; therefore, it has some excellent (and super-easy) formatters.

Inside a DataGrid, what seems to be a problem for so many is the fact that the data is seemingly rendered without any input from the developer. All this means, however, is that you need to write your very own, custom item renderer.

Sounds scary? It couldn’t be simpler: all you have to do is create a mini-component inside your DataGrid’s rows. For example, consider a simple DataGrid whose data provider contains the two columns orderDate (a date) and and orderAmount (a currency value). Normally, your code would probably look like this:

<mx:DataGrid dataProvider="{data}" width="100%">
  <mx:columns>
    <mx:DataGridColumn headerText="Order Date" dataField="orderDate" />
    <mx:DataGridColumn headerText="Order Amount" dataField="orderAmount" />
  </mx:columns>
</mx:DataGrid>
Obviously, there is nothing wrong with this code, which will work just fine… except that the output is not quite as user-friendly as it could be:

I don’t know about you, but my clients don’t normally work in GMT—and those crazy devils just insist that their amounts must be properly formatted with currency signs and thousand separators.

Luckily, this can be rather easily achieved using two of Flex’s many built-in data formatters—namely, mx.DateFormatter and mx.CurrentyFormatter. DateFormatter takes a string specification like “YYYY-MM-DD” and uses it to format a date. CurrencyFormatter, on the other hand, provides a number of parameters, like precision and thousand separators, that can be used to control the textual representation of a currency value. For example, we could write our two formatters like this:

<mx:DateFormatter id="dateFormatter" formatString="YYYY/MM/DD" />
<mx:CurrencyFormatter id="moneyFormatter" currencySymbol="$" useThousandsSeparator="true" precision="2"/>

Remember that both formatters are non-visual elements and, therefore, must be added to the <fx:Declarations> element of your component.

All that remains to be done now is to create item renderers to provide a custom representation of each of the DataGrid’s columns:

<mx:DataGrid dataProvider="{data}" width="100%">
  <mx:columns>
    <mx:DataGridColumn headerText="Order Date" dataField="orderDate">
      <mx:itemRenderer>
        <fx:Component>
          <mx:Label text="{outerDocument.dateFormatter.format(data.orderDate)}" />
        </fx:Component>
      </mx:itemRenderer>
    </mx:DataGridColumn>
    <mx:DataGridColumn headerText="Order Amount" dataField="orderAmount">
      <mx:itemRenderer>
        <fx:Component>
          <mx:Label text="{outerDocument.moneyFormatter.format(data.orderAmount)}" width="100%" textAlign="right" />
        </fx:Component>
      </mx:itemRenderer>
    </mx:DataGridColumn>
  </mx:columns>
</mx:DataGrid>
As you can see, we simply use <fx:Component> to declare an inline component inside the itemRenderer attribute of each column. Once instantiated, the custom component will have its own scope, but we can still access the data row whose values we must visualize through the data property, while the main component (where our formatters reside) is stored in the outerDocument property. Note that I am using <mx:Label> and not <s:Label>, because the DataGrid is a Halo container and, therefore, most Spark components cannot be fit into it1. For good measure, I also-right aligned the currency column; note that I am setting the label’s width to 100 percent so that it will stretch all the way to the end of the column; otherwise, the right-alignment will be ineffective because the label will only occupy as much space as necessary.

That’s it2! The output of our little application has improved considerably:

Now, we could, in theory, have placed the formatters into the individual item renderers, as long as we enclose them in their own <fx:Declarations> block:
<mx:DataGridColumn headerText="Order Date" dataField="orderDate">
  <mx:itemRenderer>
    <fx:Component>
      <mx:Label text="{outerDocument.dateFormatter.format(data.orderDate)}">
        <fx:Declarations>
          <mx:DateFormatter id="dateFormatter" formatString="YYYY/MM/DD" />
        </fx:Declarations>
      </mx:Label>
    </fx:Component>
  </mx:itemRenderer>
</mx:DataGridColumn>
This will arguably keep our code a little cleaner and make it easier to customize multiple columns to different formatting specifications. However, the added flexibility would come at the cost of creating an additional object for each item renderer for each row that is displayed at any given time.

  1. Ironically, <s:Label> will actually work here, but the compiler will complain about the fact that the data property cannot be accessed. That’s because Spark’s version of the label is not designed for use as an item renderer… which unfortunately is not mentioned in the error reported to you.
  2. Note, most interestingly, that we do not have to worry about the bindability of each row’s individual properties—that’s because the DataGrid object actively invalidates the data property of the item renderers for each row that needs to be displayed.