Thursday, July 26, 2007

I'm back on the flex bandwagon!

Now I'm working for a company in San Diego, and I've made a very beautiful reporting engine for them. As I'm going through my code wanting to make it even easier to work with and make future reports, I came across the DataGridColumn itemRenderer - which imo was implemented incorrectly by adobe.
The problem: You want to pass the field to render in and reuse the same renderer many times, but theres no way to do that!

So, I found a few solutions online, but modified them slightly to require no actionscript coding within the UI component itself (a practice of mine I'm trying my damnedest to stick to, as it forces me to write simple reusable classes, though not always clean and easy to understand the internals of).

The result I aimed for was being able to do this:

<itemRenderers:MoneyRenderer field="roomRevenue" datagridcolumn="{this.roomRevenueDataGridColumn}"/>

roomRevenueDataGridColumn is the id of a datagridcolumn in some datagrid in the report, and "roomRevenue" is the XML field we want to be given a dollar sign and comma separators with two decimal spaces.

And here is MoneyRenderer.as file that enables this:

package com.somecompany.reports.itemRenderers
{
import mx.core.IFactory;
import mx.controls.dataGridClasses.DataGridColumn;
/*
Class used for converting a number to a dollar preceded, comma delimited, 2 decimal formatted string.
Sample Usage:
<itemrenderers:moneyrenderer id="theMoneyRenderer" field="roomRevenue" datagridcolumn="{this.roomRevenueDataGridColumn}"/>
*/
public class MoneyRenderer implements mx.core.IFactory
{
public var field:String;
public function set dataGridColumn(dgc:DataGridColumn):void {
dgc.itemRenderer=this;
}
public function newInstance():* {
return new MoneyClass(field);
}
}
}
import mx.controls.Text;
import mx.formatters.CurrencyFormatter;

class MoneyClass extends mx.controls.Text
{
public var field:String;
public function MoneyClass(f:String):void {
field=f;
}
override public function set data(value:Object):void {
super.data = value;

var cf:CurrencyFormatter = new CurrencyFormatter;
cf.currencySymbol = "$";
cf.precision = 2;

if (value != null) {
this.text = cf.format(value[field]);
}
super.invalidateDisplayList();
}
}

4 comments:

Patrick Ryan said...

Could also provide an example usage within a DataGrid. ItemRenderers seem to be much harder than they should and I agree with your comment about them being broken.

I am sure this is simple, I am just not seeing it yet.

Thanks for the post.

Seth Caldwell said...

Just make a datagrid, and give one column an id
<itemRenderers:MoneyRenderer field="thedatafield" datagridcolumn="{this.someDataGridColumn}"/>
<yourdatagridmxml>
<dataGridColumn id="someDataGridColumn"/>
</yourdatagridmxml>
However, flex 3 and the advancedDataGrid have fixed the need for this. Now you can reference the field property in the renderer and do currentitem[field] to get at it ;)

Coke said...

I am getting this error
The prefix "itemRenderers" for element "itemRenderers:MoneyRenderer" is not bound.
any idea of whats wrong
?

Coke said...

the problem was solve writing in the main application tag
mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:itemRenderers="itemRenderers.*"......

Tks.