Categories: Programming, Flex Posted by mharman on 3/16/2009 12:44 AM | Comments (0)

I'm in the process of preparing a presentation on using various data grids and I came upon an interesting issue. I'm new to Flex and have not fully explored AS3 - in other words I'm a bit of a noob. I want to bring data into my Flex application from a Remote Object and assign the data to two bindable data variables. Variable#1 would be bound to grid#1 and variable#2 would be bound to grid#2. This was my initial approach:

 

CFC

<cfcomponent output="false">
<cffunction name="getDemoData" access="remote" output="false" returntype="query">
<cfset var query = queryNew("Region,Territory,Territory_Rep,Actual,Estimate",

"varchar,varchar,varchar,decimal,decimal") />

<cfset queryAddRow(query) />
<cfset querySetCell(query,"Region","Southwest") />
<cfset querySetCell(query,"Territory","Arizona") />
<cfset querySetCell(query,"Territory_Rep","Barbara Jennings") />
<cfset querySetCell(query,"Actual",38865) />
<cfset querySetCell(query,"Estimate",40000) />

<cfset queryAddRow(query) />
<cfset querySetCell(query,"Region","Southwest") />
<cfset querySetCell(query,"Territory","Arizona") />
<cfset querySetCell(query,"Territory_Rep","Dana Binn") />
<cfset querySetCell(query,"Actual",29885) />
<cfset querySetCell(query,"Estimate",30000) />

<cfset queryAddRow(query) />
<cfset querySetCell(query,"Region","Southwest") />
<cfset querySetCell(query,"Territory","Central California") />
<cfset querySetCell(query,"Territory_Rep","Joe Smith") />
<cfset querySetCell(query,"Actual",29134) />
<cfset querySetCell(query,"Estimate",30000) />

<cfset queryAddRow(query) />
<cfset querySetCell(query,"Region","Southwest") />
<cfset querySetCell(query,"Territory","Nevada") />
<cfset querySetCell(query,"Territory_Rep","Bethany Pittman") />
<cfset querySetCell(query,"Actual",52888) />
<cfset querySetCell(query,"Estimate",45000) />

<cfset queryAddRow(query) />
<cfset querySetCell(query,"Region","Southwest") />
<cfset querySetCell(query,"Territory","Northern California") />
<cfset querySetCell(query,"Territory_Rep","Lauren Ipsum") />
<cfset querySetCell(query,"Actual",38805) />
<cfset querySetCell(query,"Estimate",40000) />

<cfset queryAddRow(query) />
<cfset querySetCell(query,"Region","Southwest") />
<cfset querySetCell(query,"Territory","Northern California") />
<cfset querySetCell(query,"Territory_Rep","T.R. Smith") />
<cfset querySetCell(query,"Actual",55498) />
<cfset querySetCell(query,"Estimate",40000) />

<cfset queryAddRow(query) />
<cfset querySetCell(query,"Region","Southwest") />
<cfset querySetCell(query,"Territory","Northern California") />
<cfset querySetCell(query,"Territory_Rep","Alice Treu") />
<cfset querySetCell(query,"Actual",44985) />
<cfset querySetCell(query,"Estimate",45000) />

<cfset queryAddRow(query) />
<cfset querySetCell(query,"Region","Southwest") />
<cfset querySetCell(query,"Territory","Northern California") />
<cfset querySetCell(query,"Territory_Rep","Jane Grove") />
<cfset querySetCell(query,"Actual",44913) />
<cfset querySetCell(query,"Estimate",45000) />

<cfreturn query />
</cffunction>
</cfcomponent>

Flex

<?xml  version="1.0"  encoding="utf-8"?>
<mx:Application  xmlns:mx="http://www.adobe.com/2006/mxml"  
layout="absolute"  
creationComplete="initApp()">
<mx:Script>
<![CDATA[
import  mx.rpc.events.ResultEvent;
import  mx.rpc.events.FaultEvent;
import  mx.collections.ArrayCollection;
import  mx.controls.Alert;

[Bindable]
private  var  gridData1:ArrayCollection;

[Bindable]
private  var  gridData2:ArrayCollection;

private  function  initApp():void  {
this.gridDataSource.queryAsArray();
}

/*  result  handlers  */
private  function  gridDataHandler(e:ResultEvent):void  {
gridData1  =  e.result  as  ArrayCollection;
gridData2  =  e.result  as  ArrayCollection;
}

/*  fault  handlers  */
private  function  faultHandler(fault:FaultEvent):void  {
Alert.show(fault.message.toString());
}
]]>
</mx:Script>

<mx:RemoteObject  id="gridDataSource"  
destination="ColdFusion"  
source="CFGrid.CFC.DemoData"  
fault="faultHandler(event)">
<mx:method  name="getDemoData"  result="gridDataHandler(event)"  />
</mx:RemoteObject>

<mx:Panel  title="Grid1">
<mx:DataGrid  id="myDataGrid1"  
width="600"  
height="200"  
dataProvider="{gridData1}">
<mx:columns>
<mx:DataGridColumn  dataField="Region"  headerText="Region"  />
<mx:DataGridColumn  dataField="Territory"  headerText="Territory"  />
<mx:DataGridColumn  dataField="Territory_Rep"  headerText="Rep"  />
<mx:DataGridColumn  dataField="Actual"  headerText="Actual"  />
<mx:DataGridColumn  dataField="Estimate"  headerText="Estimate"  />
</mx:columns>
</mx:DataGrid>
</mx:Panel>

<mx:Panel  title="Grid2"  x="0"  y="250">
<mx:DataGrid  id="myDataGrid1"  
width="600"  
height="200"  
dataProvider="{gridData2}">
<mx:columns>
<mx:DataGridColumn  dataField="Region"  headerText="Region"  />
<mx:DataGridColumn  dataField="Territory"  headerText="Territory"  />
<mx:DataGridColumn  dataField="Territory_Rep"  headerText="Rep"  />
<mx:DataGridColumn  dataField="Actual"  headerText="Actual"  />
<mx:DataGridColumn  dataField="Estimate"  headerText="Estimate"  />
</mx:columns>
</mx:DataGrid>
</mx:Panel>
</mx:Application>

The problem with this approach is that when the grid is rendered, if you make a change to one grid, the other automatically changes as well. For example, if you change the sort order in one grid, the sort order in the other grid will also change. This is not what I am wanting. It quickly became clear to me that this is because both gridData1 and gridData2 are references to the same data. So the question is, how do I create separate instances of the data and assign each instance to a separate variable?

My solution is to modify the CFC and create a array of structures from the query. An array is then passed back to Flex, not an ArrayCollection. This allows me to create a new temporary variable of type Array and assign the result to it, then create a new instance of an ArrayCollection with the temporary variable passed in the constructor. This will work for my presentation, however as Paul, a friend of mine, pointed out, on a large record set, the overhead to process the records into an array is expensive and would not be a good option.

 

New CFC Function

<cffunction name="queryAsArray" access="remote" output="true" returntype="array">
<cfset var query = getDemoData() />
<cfset var array = ArrayNew(1) />

<cfloop query="query">
<cfset array[query.CurrentRow]["Region"] = query.Region />
<cfset array[query.CurrentRow]["Territory"] = query.Territory />
<cfset array[query.CurrentRow]["Territory_Rep"] = query.Territory_Rep />
<cfset array[query.CurrentRow]["Actual"] = query.Actual />
<cfset array[query.CurrentRow]["Estimate"] = query.Estimate />
</cfloop>

<cfreturn array />
</cffunction>

Flex Changes

private function arrayDataHandler(e:ResultEvent):void {
var tempData1:Array = new Array();
var tempData2:Array = new Array();

tempData1 = e.result as Array;
gridData = new ArrayCollection(tempData1);

tempData2 = e.result as Array;
advancedGridData = new ArrayCollection(tempData2);
}

...

<mx:RemoteObject id="gridDataSource" 
destination="ColdFusion" 
source="CFGrid.CFC.DemoData" 
fault="faultHandler(event)">
<mx:method name="getDemoData" result="gridDataHandler(event)" />
<mx:method name="queryAsArray" result="arrayDataHandler(event)" />
</mx:RemoteObject>

There may be a better way of doing this, in fact I'm almost positive there is. If you have a better way, please post, I'm interested in how others are solving this problem.

UPDATE Thanks to Paul Kukiel and the Ntt.cc site, I have implemented a solution in a different way. Instead of processing the data in the CFC, I use the following code to process it in my event handler in Flex:

private function gridDataHandler(e:ResultEvent):void {
gridData = new ArrayCollection();
advancedGridData = new ArrayCollection();

for each ( var i:Object in e.result ){ 
gridData.addItem(i);
advancedGridData.addItem(i);
}
}