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);
}
}