Sunday, November 21, 2010

Nested AdvancedDataGrid

Flex them Grids! (AdvancedDataGrid as a subgrid with flat data)

A grid inside a grid is a common requirement; I thought. Yet, I scoured the internet for a solution and I couldn’t find it anywhere (…at least not something that could be done with “flat data”). The following is something I wanted to achieve.

Adobe LiveDocs show an example here where they put a chart in an itemrenderer. However they show this example with “Hierarchical” collection. Their data structure looks like:
   1: [Bindable]
   2:      private var dpHierarchy:ArrayCollection= new ArrayCollection([
   3:        {name:"Barbara Jennings", region: "Arizona", total:70, children:[  
   4:          {detail:[{amount:5},{amount:10},{amount:20},{amount:45}]}]},
   5:        {name:"Dana Binn", region: "Arizona", total:130,  children:[ 
   6:          {detail:[{amount:15},{amount:25},{amount:35},{amount:55}]}]},
   7:        {name:"Joe Smith", region: "California", total:229,  children:[ 
   8:          {detail:[{amount:26},{amount:32},{amount:73},{amount:123}]}]},
   9:        {name:"Alice Treu", region: "California", total:230, children:[ 
  10:          {detail:[{amount:159},{amount:235},{amount:135},{amount:155}]}
  11:        ]}
  12:      ]);      

However, this is almost never how one would get data from the server. We generally get a flat data-structure as ArrayCollection. The the trick would be to convert this flat structure into a Hierarchy of objects.

   1: private var _BaseData:ArrayCollection =  new ArrayCollection([
   2:     {region:'south', state:'FL', city:'Alachua', population:'6,098'},
   3:     {region:'south', state:'FL', city:'Alford', population:'466'},
   4:     {region:'south', state:'FL', city:'Altha', population:'506'},
   5:     {region:'south', state:'FL', city:'Altamonte', population:'41,200'},
   6:     {region:'south', state:'TX', city:'Addison', population:'14,166'},
   7:     {region:'south', state:'TX', city:'Perezville', population:'5,444'},
   8:     {region:'south', state:'TX', city:'Alamo', population:'14,760'},
   9:     {region:'north', state:'NY', city:'Airmont', population:'7,799'},
  10:     {region:'north', state:'NY', city:'Akron', population:'3,085'},
  11:     {region:'north', state:'NY', city:'Alabama', population:'1,881'},
  12:     {region:'north', state:'NY', city:'Albany', population:'95,658'}
  13: ]);
The converted object should now look like:

   1: private var _HData:ArrayCollection =  new ArrayCollection([
   2:     {region:'south', state:'FL', children:[{detail:[
   3:             {city:'Alachua', population:'6,098'},
   4:             {city:'Altamonte', population:'41,200'},
   5:             {city:'Alford', population:'466'},
   6:             {city:'Altha', population:'506'}
   7:         ]}
   8:     ]},
   9:     {region:'south', state:'TX', children:[{detail:[
  10:             {city:'Perezville', population:'5,444'},
  11:             {city:'Addison', population:'14,166'},
  12:             {city:'Alamo', population:'14,760'}
  13:         ]}
  14:     ]},
  15:     {region:'north', state:'NY', children:[
  16:         {detail:[
  17:                 {city:'Airmont', population:'7,799'},
  18:                 {city:'Akron', population:'3,085'},
  19:                 {city:'Alabama', population:'1,881'},
  20:                 {city:'Albany', population:'95,658'}
  21:             ]
  22:         }
  23:     ]}
  24: ]);

The following function uses a cursor to iterate over this ArrayCollection, read the city and population, creates a child object. Then it discards the repeated rows.

public function setHierarchy():void {
// sort first
var sort:Sort= new Sort();
sort.fields = [new SortField('state',false)];
_BaseData.sort = sort;

var HData:ArrayCollection = new ArrayCollection();
var csr:IViewCursor = _BaseData.createCursor();

var previousState:String = "";
var counter:int = 0;
var children:Array = [];

while(!csr.afterLast) {
// flag to remove repeated rows
var addToHierarchy:Boolean = false;
var subGridObj:Object = {city:'',population:''}; =;
subGridObj.population = csr.current.population;

// find next/previous values and compare with current record
var str:String = csr.current.state; 
if (previousState != str) {
children = [];
previousState = str;
// flag to remove repeated rows
addToHierarchy = true;
//create child objects and populate with child-data
csr.current.children = [];
csr.current.children[counter] = {detail:children};
// copy to Hierarchy and neglect repeated rows
if (addToHierarchy) {

// finally make assignment as HierarchicalData
adg1.dataProvider = new HierarchicalData(HData);

The AdvancedDataGrid declaration should now take an ItemRenderer column

<mx:AdvancedDataGrid id="adg1" x="200" width="600" height="400" variableRowHeight="true"
<mx:AdvancedDataGridColumn headerText="Region" dataField="region" />
<mx:AdvancedDataGridColumn headerText="State" dataField="state" />

columnSpan="0" columnIndex="1"/>         

and the itemrenderer would be composed of a grid:

<?xml version="1.0" encoding="utf-8"?>
<s:MXAdvancedDataGridItemRenderer xmlns:fx="" 

<s:VGroup paddingBottom="10" paddingLeft="10" paddingTop="10">
<mx:AdvancedDataGrid id="subAdg" dataProvider="{data.detail}">



FlashFlexNewbie said...

This is very useful. Basically we have to reorganize data

Anonymous said...

Love the Kramer pic on your blog. Nicely done..