Wednesday, May 28, 2008

Grouping XML data using GroupingCollection

I was under the impression this doesn't require any sample as it should be very easy to write one. But frequent questions on the forums have resulted in this post.

There is not much to describe so :

<mx:XML id="inputData" >
 <Tulokset>
 <Tulos id="xxx" group="xx" />
 <Tulos id="xxx" group="xx" />
 <Tulos id="xxx" group="xx" />
 </Tulokset>
</mx:XML> 
 
<mx:AdvancedDataGrid creationComplete="gc.refresh()">
 <mx:dataProvider>
  <mx:GroupingCollection source="{inputData.Tulos}" id="gc">
   <mx:Grouping>
    <mx:GroupingField name="@group" />
   </mx:Grouping>
  </mx:GroupingCollection>
 </mx:dataProvider>
 <mx:columns>
  <mx:AdvancedDataGridColumn dataField="@id" />
 </mx:columns>
</mx:AdvancedDataGrid>

The source is here.

Update: The below example shows how to use grouping when the data is in child nodes instead of attributes. This is a bit tricky because GroupingCollection is derived from HierarchicalData which treats child nodes as children by default. This leads to problems when we try to display the data after grouping. The trick is to make use of the childrenField property and point it to something like "undefined".

<mx:XML id="inputData" >
 <Tulokset>
  <Tulos>
     <id>xx1</id>
     <group>xxx</group>
  </Tulos>
  <Tulos>
     <id>xx2</id>
     <group>xxx</group>
  </Tulos>
  <Tulos>
     <id>xx3</id>
     <group>xxx</group>
  </Tulos>
 </Tulokset>
</mx:XML> 
 
<mx:AdvancedDataGrid creationComplete="gc.refresh()">
 <mx:dataProvider>
  <mx:GroupingCollection source="{inputData.Tulos}" id="gc" childrenField="undefined">
   <mx:Grouping>
    <mx:GroupingField name="group"  />
   </mx:Grouping>
  </mx:GroupingCollection>
 </mx:dataProvider>
 <mx:columns>
  <mx:AdvancedDataGridColumn dataField="id" />
 </mx:columns>
</mx:AdvancedDataGrid>

The source is here.

Please note that childrenField is also helpful when we are trying to display XML data with data and children as child nodes. Use it to point to the proper node name which contains the children. In the following example we need to set childrenField="details" to work properly.


<mx:XML id="special">
<rows>
<row>
  <name>A</name>
  <fund>100</fund>
  <details>
   <row>
   <name>a</name>
   <fund>20</fund>
   </row>
   <row>
   <name>b</name>
   <fund>80</fund>
   </row>
  </details>
</row>
<row>
  <name>C</name>
  <fund>200</fund>
  <details>
   <row>
   <name>a</name>
   <fund>80</fund>
   </row>
   <row>
   <name>b</name>
   <fund>80</fund>
   </row>
   <row>
   <name>c</name>
   <fund>40</fund>
   </row>
  </details>
</row>
</rows> 
</mx:XML>

<mx:AdvancedDataGrid >
   <mx:dataProvider>
       <mx:HierarchicalData source="{special.row}" childrenField="details" >
       </mx:HierarchicalData>
   </mx:dataProvider>
 <mx:columns>
  <mx:AdvancedDataGridColumn dataField="name" />
  <mx:AdvancedDataGridColumn dataField="fund" />
 </mx:columns>
</mx:AdvancedDataGrid>

Object/Array and ObjectProxy/ArrayCollection binding problems

Take a look at discussion here.

Many seems to be stumble upon this problem. Very similar to Object not dispatching any event for property value changes Array also doesn't dispatch any event for items added/deleted from it. Due to this when an object or array is used in the dataProvider of a list based control or comboBox etc any updates to the object or array would not get reflected in the UI.

The following sample should make it clear.

The first DG is fed with a ArrayCollection of Objects and the second DG has ObjectProxies. Select a item in the DGs and edit the values displayed in the text inputs. To complete the editing hit "Enter". Notice that in the first DG the values don't get reflected immediately where as in second DG it does.

To support Object based dataProviders the list based controls in Flex make a explicit call to ICollectionView.itemUpdated() to force a change event. This is the reason edits in list based controls get propagated to other list based controls when both of them are using the same colletion as input.

The source is here.

As noted in the forum discussion, when HierarchicalData is built using Objects/Arrays (to represent children) the changes to the Array would not get propagated to ADG. Hence it is necessary to construct the HierarchicalData using ObjectProxies and ArrayCollections.

Thursday, May 15, 2008

Fixing itemRenderer memory leak in AdvancedDataGrid on columns change

Please read the discussion about memory leak here.

I think I found the workaround for this issue. We need to override the set columns method in a extended ADG class and add the code to free the dictionary holding on to the itemRenderers. Here is the code.

<?xml version="1.0" encoding="utf-8"?>
<mx:AdvancedDataGrid xmlns:mx="http://www.adobe.com/2006/mxml">
 <mx:Script>
  <![CDATA[
   override public function set columns(value:Array):void
   {
    super.columns = value;
    itemRendererToFactoryMap = new Dictionary(false);
   }
  ]]>
 </mx:Script>
</mx:AdvancedDataGrid>