Monday, December 17, 2007

Extending the Flex Currency Formatter

I was faced with a puzzling challenge this week on a Flex app I've been working on. I have a Flex DataGrid that is showing financial metrics - projected vs actual and the variance...a very common sales type of report. It breaks these metrics down by year, quarter and month.


Imagine it's September. Now you'll see in the grid above that October through December show 0's for the actual column, and a negative variance, but they are just months that haven't happened yet. One requirement we had for how these metrics display was that for time periods that haven't happened yet this year (IE next month, next quarter), we show a dash in the grid instead of a 0. It's all about the psychology of not making the report look overly negative :)

The challenge was that the way these numbers are displaying within the grid is via an ItemRenderer with a CurrencyFormatter. From within that item renderer, I don't have any access to data columns outside the immediate column. For instance, I could not refer to the Month column. Moreover, if I ever returned a value for that column that was a string and not numeric (I'm using a CFC on the back end), I couldn't use the CurrencyFormatter to nicely format my numbers with dollar signs and thousands separators.

I mulled over several evil sounding ways to make this work, and as I went to implement one of them, a much simpler solution struck me.

1. Within the CFC where I'm creating a collection of objects, I have access to other columns of data within the grid. Thus I can evaluate if the time period is in the future with a just a line of code.
2. Rather than returning the actual character I want to display (which won't work with the formatter), I replace the actual value of the "future" data with -1. This way, the data is still numeric, but it's not a number that will ever happen naturally. This seems to be a common programming trick - using negative numbers for "non-real" values. But because it's numeric, I can still use the currency formatter on the front end.
3. I create a custom ActionScript formatter on the front end which extends the CurrencyFormatter class. This formatter has two extra properties: replaceValue and replaceChar. replaceValue is the "fake" value I'm looking to replace, replaceChar is the character I want to replace it with . That way I can customize it at any time if say, we decide we want an x instead of a dash. Then within the formatter, I just compare the actual value with the replaceValue, and if they're equal, I return the replaceChar instead of the formatted string. Otherwise, I return the normal formatted string.

Here's the completed code for my ForecastCurrencyFormatter solution.

ColdFusion/Back End

<cfif dateOfMetric GT month(now())>

<cfset actualAmount = -1>

<cfset varianceAmount = -1>

</cfif>


Flex Formatter



//ForecastCurrencyFormatter.as

package com.myFormatters {

import mx.formatters.CurrencyFormatter;
public class ForecastCurrencyFormatter extends CurrencyFormatter {

/*value to replace - defaults to -1*/

public var replaceValue:Number = -1;

/*character to replace with - defaults to a dash*/

public var replaceChar:String = '-';



public function ForecastCurrencyFormatter() {

super();

}



//override the format method with my custom formatting

override public function format(value:Object):String {

/*if the number value is the value to replace, return the replacement character instead*/

if (value == replaceValue) {

return replaceChar;

}

/*otherwise just format according to the standard currency formatter*/

else {

return super.format(value);

}

}



}

}


Using the Formatter


<!-- at the root, declare a namespace for the formatter components -->

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

<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:fm="com.myFormatters.*">
<!-- this is just one column within my datagrid, showing use of the formatter -->

<mx:DataGridColumn textAlign="right" headerText="Actual" sortable="false" dataField="actual">

<mx:itemRenderer>

<mx:Component>

<mx:VBox clipContent="false">

<fm:ForecastCurrencyFormatter replaceValue="-1" replaceChar="-" id="fcf" precision="0" thousandsSeparatorTo="," currencySymbol="$" alignSymbol="left" />

<mx:Text width="100%" text="{fcf.format(data.actual)}" />

</mx:VBox>

</mx:Component>

</mx:itemRenderer>

</mx:DataGridColumn>

That's about it. We now have a formatter that behaves just like the standard currency formatter, but with an additional formatting option.

1 comment:

AndyChou said...
This comment has been removed by a blog administrator.