This afternoon a user IMed me to ask for help with a ColdFusion powered Flex 2 application. He wanted to be able to perform on-the-fly filtering of data in a CF populated Flex DataGrid control. Here is the solution I sent him.
Populate the DataGrid with an ArrayCollection (converting the data to an ArrayCollection if needed). ArrayCollection has a property named filterFunction (well, technically filterFunction is a property of listCollectionView of which ArrayCollection is a subclass) which takes the name of a function to be used to filter data. Each time ArrayCollection contents are refreshed this passed function will be called, once per ArrayCollection item. If the function returns true then the item is included, if false then it is excluded. So, to filter DataGrid contents on-the-fly, simple define a filter function that performs the filtering you need, and call ArrayCollection.refresh() as the filter text changes.
Here is a complete example. In the interests of simplicity this uses a local ArrayCollection (as opposed to data returned from ColdFusion) and will run as is. ArrayCollection myData is the dataProvider for the DataGrid. Function processFilter() is the filter function, it accepts an object and returns true or false based on whether it is a match or not (this filter function looks for substrings in a single column, and this could be changed of course). The TextInput box is where filter text is entered, and the change event simply calls myData.refresh() which forces the filter function to be reapplied to the ArrayCollection.
It is worth noting that filterFunction should not be specified until the ArrayCollection has been populated (or you'll get a null reference error). So, if you are using RemoteObject to invoke a CFC method to return data to populate an ArrayCollection, you should not set filterFunction until data has been returned. As such, you may want to do this in a RemoteObject result handler.
Here's the code:
<?xml version=
"1.0" encoding=
"utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="vertical" creationComplete="initApp()"> <mx:Script> <![CDATA[
// On startup public function initApp():void
{
// Set filter function // Be careful to set filterFunction // only after ArrayCollection has been // populated. myData.filterFunction=processFilter;
}
// Filter function public function processFilter(item:Object):Boolean
{
var result:Boolean=false;
// If no filter text, or a match, then true if (!item.name.length
|| item.name.toUpperCase().indexOf(txtFilter.text.toUpperCase()) >= 0)
result=true;
return result;
}
]]>
</mx:Script> <!-- Data (use ArrayCollection) --> <mx:ArrayCollection id="myData"> <mx:source> <mx:Object name="Ben Forta" location="Oak Park, MI" phone="(248)555-5555" /> <mx:Object name="Jane Doe" location="New York, NY" phone="(212)555-1234" /> <mx:Object name="Jim Jones" location="Atlanta, GA" phone="(414)555-1212" /> <mx:Object name="Roberta Roberts" location="Chicago, IL" phone="(312)555-4321" /> <mx:Object name="Steve Stevens" location="Boston, MA" phone="(617)555-5656" /> </mx:source> </mx:ArrayCollection> <!-- UI --> <mx:HBox width="100%"> <mx:Label text="Filter:"/> <mx:TextInput id="txtFilter" width="100%"
change="myData.refresh()"/> </mx:HBox> <mx:DataGrid dataProvider="{myData}"
width="100%" height="100%"> <mx:columns> <mx:DataGridColumn headerText="Name"
dataField="name"/> <mx:DataGridColumn headerText="Location"
dataField="location"/> <mx:DataGridColumn headerText="Phone"
dataField="phone"/> </mx:columns> </mx:DataGrid> </mx:Application>
Oliver
So, I made a stupid sample.
http://shigeru-nakagaki.com/index.cfm/2006/7/15/20...
--- Ben
fyi - clicking on comments(#) on a blog entry 'popups' comment window (where I am writing this) but does not show comments on top when they exists...
here is my grid component code:
<?xml version="1.0" encoding="utf-8"?>
<cfComponents:SizeableTitleWindow xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*"
xmlns:cfComponents="com.adobe.ColdFusion.components.*"
creationComplete="initComponent()"
title="Legend"
showCloseButton="true" close="WindowManager.remove(this);" >
<cfComponents:states>
<mx:State name="find">
<mx:AddChild relativeTo="{hbox1}" position="before">
<mx:HBox width="100%">
<mx:Label text="Search"/>
<mx:TextInput id="txtFilter"/>
<mx:Button label="Button"/>
</mx:HBox>
</mx:AddChild>
</mx:State>
</cfComponents:states>
<mx:Script>
<![CDATA[
import com.adobe.windowedApplication.managers.WindowManager;
import mx.core.UIComponent;
import mx.collections.ArrayCollection;
import mx.utils.ObjectUtil;
import mx.controls.Alert;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import com.cfgenerated.views.detail.detail;
private var currentIndex:int = 0;
private var _key:Object;
[Bindable]
public function get key():Object
{
return this._key;
}
public function set key(key:Object):void
{
this._key = key;
}
private function initComponent():void
{
refreshList(null);
}
public function refreshList(event:Event):void
{
this.dataManager.getMasterQuery(this.key);
}
private function deleteItem(key:Object):void
{
this.dataManager.deleteItem(key);
}
private function getDetailComponent(key:Object):UIComponent
{
var view:detail = new detail();
view.key = key;
view.addEventListener("change", refreshList);
return view;
}
/**
* RemoteObject result and error handlers
*/
private function server_fault(event:FaultEvent):void
{
// dump error message
Alert.show( ObjectUtil.toString(event.fault) );
}
private function getMasterQuery_result(event:ResultEvent):void
{
//Alert.show( ObjectUtil.toString(event.result) );
this.masterList.dataProvider = event.result as ArrayCollection;
this.masterList.selectedIndex = this.currentIndex;
}
private function deleteItem_result(event:ResultEvent):void
{
//Alert.show( ObjectUtil.toString(event.result) );
refreshList(null);
}
]]>
</mx:Script>
<mx:RemoteObject
id="dataManager"
showBusyCursor="true"
destination="ColdFusion" source="Legend.components.cfgenerated.Legend">
<mx:method name="getMasterQuery" result="getMasterQuery_result(event)" fault="server_fault(event)" />
<mx:method name="deleteItem" result="deleteItem_result(event)" fault="server_fault(event)" />
</mx:RemoteObject>
<mx:HBox id="addEditDeleteToolbar">
<mx:Spacer width="100%"/>
<mx:Button id="addBtn"
icon="@Embed(source='../../../../images/add.png')" width="40"
click="WindowManager.add( getDetailComponent(null), this, false );" toolTip="Add" />
<mx:Button id="editBtn"
icon="@Embed(source='../../../../images/edit.png')" width="40"
enabled="{this.masterList.selectedIndex>=0}"
click="WindowManager.add( getDetailComponent(this.masterList.selectedItem.ID), this, false );" toolTip="Edit" />
<mx:Button id="deleteBtn"
icon="@Embed(source='../../../../images/delete.png')" width="40"
enabled="{this.masterList.selectedIndex>=0}"
click="this.deleteItem(this.masterList.selectedItem.ID)" toolTip="Delete" />
<mx:Button id="printBtn"
icon="@Embed(source='../../../../images/print.png')" width="40"
click="navigateToURL(new URLRequest('http://192.168.10.18/lookup/printabbrev.cfm'));" toolTip="Print" />
<mx:Button id="searchBtn"
icon="@Embed(source='../../../../images/search.png')" width="40"
click="currentState='find'" toolTip="Search" />
</mx:HBox>
<mx:HBox width="100%" height="100%" id="hbox1">
<mx:DataGrid
id="masterList"
doubleClickEnabled="true"
doubleClick="this.currentIndex=this.masterList.selectedIndex; if(this.masterList.selectedItem!=null) { WindowManager.add( getDetailComponent(this.masterList.selectedItem.ID), this, false ); }"
width="100%" height="400" top="0" left="0" right="0" bottom="0">
<mx:columns>
<mx:DataGridColumn dataField="abbrev" headerText="Abbrev" width="100" />
<mx:DataGridColumn dataField="short_desc" headerText="Short Desc" width="400" />
</mx:columns>
</mx:DataGrid>
</mx:HBox>
</cfComponents:SizeableTitleWindow>
I am using remoteobject to get the data in the grid and used the filterfunction in the result handler, however i'm getting Cannot access a property or method of a null object reference.
I thought this was supose to prevent this error.
Thanks,
Joseph.
--- Ben
Heres the code that i use i dont know whats going on with this but i get the error no matter what i do. the data displays ok in the grid, its only when i type something in the text field.
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.rpc.events.ResultEvent;
import mx.controls.Alert;
[Bindable]
public var gridData:ArrayCollection = null;
// component init
private function initComponent():void {
dataService.test();
}
public function processFilter(item:Object):Boolean {
var result:Boolean=false;
// If no filter text, or a match, then true
if (!item["city"].length || item["city"].toUpperCase().indexOf(txtSearchLog.text.toUpperCase()) >= 0)
result=true;
return result;
}
private function getData_resultHandler(event:ResultEvent):void {
gridData = event.result as ArrayCollection;
dataGrid.dataProvider = gridData;
gridData.filterFunction=processFilter;
}
]]>
</mx:Script>
<mx:RemoteObject id="dataService" destination="ColdFusion" source="zonebooks.bin.components.test" showBusyCursor="true">
<mx:method name="test" result="getData_resultHandler(event)" fault="Alert.show(event.fault.message)"/>
</mx:RemoteObject>
<mx:Canvas width="100%" height="100%">
<mx:Label x="10" y="12" text="Search"/>
<mx:TextInput right="10" left="67" y="10" change="gridData.refresh();" id="txtSearchLog"/>
<mx:DataGrid id="dataGrid" bottom="10" top="38" right="10" left="10" draggableColumns="false" sortableColumns="false"/>
</mx:Canvas>
<mx:ControlBar>
<mx:Spacer width="100%"/>
<mx:Button label="Show Options" id="button1" click="parentDocument.currentState= 'Click'" />
</mx:ControlBar>
</mx:Panel>
I think this function works a bit better. The way you have written the function entering "en" in the textfield matches "Ben Forta". While this may be what the user wants, I think generally people are looking for a match from the beginning of the strings.
I'm using a RegEx here to do just that:
public function processFilter(item:Object):Boolean
{
var result:Boolean=false;
var pattern:RegExp = new RegExp("^"+txtFilter.text,"i");
// If no filter text, or a match, then true
if (!item.name.length || item.name.match(pattern))
result=true;
return result;
}
Gus
I am passing my data that is returned from the cfc into another component, so I can't set the filterFunction in the result function.
So in my componet I set a watcher
private function init():void{
watcher = ChangeWatcher.watch(this, ["regData"], dataLoaded);
}
then in the dataLoaded function set the filterFunction
private function dataLoaded(event:PropertyChangeEvent):void{
regData.filterFunction=processFilter;
watcher.unwatch();
}
It works, I am just not sure if it is good practice.
Russ
I have a grid that gets filled with data just fine. When I try and apply your sample to my grid for filteing I get am error
I am calling in the data using the mx:remoteobject with id="myData":
Cant assign operations into an RPC Service(filterFunction)
Im not sure what that means. If I continue and try to use the filter I get:
faultstring="The method 'refresh' could not be found in component"
any thoughts?
Thanks
George
I am closer to figuring this out. What I cant seem to do is figure out how to make the call from the RemoteObject into an ArrayCollection. I see how you have the ArrayCollection and if I type in what you have change the variables it works in my app. But if I use the RemoteObject to call to the cfc I fill the grid also, but It wont filter, then and I get a: Cannot access a property or methed of a null object. Error. Is there a sample of ArrayCollection vs. RemoteObject?
Here is the result function for my cfc. I have a variable regData typed as an ArrayCollection that is the dataProvider for my dataGrid
public function handleRegResult(event:ResultEvent):void{
regData = event.result as ArrayCollection;
}
Then you have all of the sorting/ filtering objects of an ArrayCollection
This should be close to what you are looking for:
public var roArray:ArrayCollection;
private function getRemoteObjectResult(event:Object):void
{
roArray = event.result as ArrayCollection;
roArray.filterFunction=processFilter;
roArray.refresh();
myDataGrid.dataProvider = roArray;
}
public function processFilter(item:Object):Boolean
{
var result:Boolean=false;
if (item.currentStatus == 1)
result=true;
return result;
}
In this example, I am filtering on roArray.currentStatus == 1
I hope this helps.
Gus
I think there is some extra stuff I dont need that is blocking this to work but im not sure. Thanks for looking.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="initApp()" >
<mx:Script>
<![CDATA[
import mx.rpc.events.ResultEvent;
import mx.controls.Alert;
import mx.collections.ArrayCollection;
[Bindable]
public var myData:ArrayCollection;
[Bindable]
public var aReturnTypes: Array = [ {label:"query", data:"query"} ];
[Bindable]
public var returnType:String="string";
public function clearAll():void{
myData=new ArrayCollection();
}
public function handleQueryResult(event:ResultEvent):void{
myData=event.result as ArrayCollection;
}
]]>
</mx:Script>
<mx:Script>
<![CDATA[
public function initApp():void
{
myData.filterFunction = processFilter;
}
public function processFilter(item:Object):Boolean
{
var result:Boolean=false;
if (!item.PDES5W.length
|| item.PDES5W.toUpperCase().indexOf(txtFilter.text.toUpperCase()) >= 0)
result=true;
return result;
}
]]>
</mx:Script>
<mx:RemoteObject
id="qResult"
destination="ColdFusion"
source="Inventory"
showBusyCursor="true"
>
<mx:method name="Inventory" result="handleQueryResult(event)" fault="Alert.show(event.fault.message)"/>
</mx:RemoteObject>
<mx:Panel title="Inventory" height="90%" width="100%"
paddingTop="5" paddingLeft="5" paddingRight="5" paddingBottom="5" cornerRadius="10" roundedBottomCorners="true">
<mx:HBox width="400">
<mx:TextInput width="100" id="txtFilter" change="myData.refresh()" />
<mx:RadioButton label="Warehouse" click="accordion.selectedIndex=0;" selected="true"/>
<mx:RadioButton label="Cash / Carry" click="accordion.selectedIndex=1;"/>
</mx:HBox>
<mx:Accordion id="accordion" width="100%" height="100%" headerHeight="5">
<mx:VBox height="100%" >
<mx:DataGrid dataProvider="{myData}" initialize="qResult.Inventory()" width="100%" height="100%">
<mx:columns>
<mx:DataGridColumn dataField="PNUM5W" headerText="Prod ID" width="55"/>
<mx:DataGridColumn dataField="PDES5W" headerText="Desc" width="163"/>
<mx:DataGridColumn dataField="SDIVDE" headerText="Size" width="50"/>
<mx:DataGridColumn dataField="ISCF3W" headerText="Pac" width="35"/>
<mx:DataGridColumn dataField="DIVN5W" headerText="Rnk" width="35"/>
<mx:DataGridColumn dataField="INV_11" headerText="Or Inv" width="50"/>
<mx:DataGridColumn dataField="ORD_11" headerText="Or Ord" width="50"/>
<mx:DataGridColumn dataField="DAT_11" headerText="Or Due" width="50"/>
<mx:DataGridColumn dataField="INT_11" headerText="Or InT" width="50"/>
<mx:DataGridColumn dataField="INV_31" headerText="Ch Inv" width="50"/>
<mx:DataGridColumn dataField="ORD_31" headerText="Ch Ord" width="50"/>
<mx:DataGridColumn dataField="DAT_31" headerText="Ch Due" width="50"/>
<mx:DataGridColumn dataField="INT_31" headerText="Ch InT" width="50"/>
<mx:DataGridColumn dataField="INV_31" headerText="MH Inv" width="50"/>
<mx:DataGridColumn dataField="ORD_31" headerText="MH Ord" width="50"/>
<mx:DataGridColumn dataField="DAT_31" headerText="MH Due" width="50"/>
<mx:DataGridColumn dataField="INT_31" headerText="MH InT" width="50"/>
</mx:columns>
</mx:DataGrid>
</mx:VBox>
<mx:VBox height="100%" >
<mx:DataGrid dataProvider="{myData}" initialize="qResult.Inventory()" width="100%" height="100%">
<mx:columns>
<mx:Array>
<mx:DataGridColumn dataField="PNUM5W" headerText="Prod ID" width="55"/>
<mx:DataGridColumn dataField="PDES5W" headerText="Desc" width="163"/>
<mx:DataGridColumn dataField="SDIVDE" headerText="Size" width="50"/>
<mx:DataGridColumn dataField="ISCF3W" headerText="Pac" width="35"/>
<mx:DataGridColumn dataField="DIVN5W" headerText="Rnk" width="35"/>
<mx:DataGridColumn dataField="INV_11" headerText="Or Inv" width="50"/>
<mx:DataGridColumn dataField="ORD_11" headerText="Or Ord" width="50"/>
<mx:DataGridColumn dataField="DAT_11" headerText="Or Due" width="50"/>
<mx:DataGridColumn dataField="INT_11" headerText="Or InT" width="50"/>
<mx:DataGridColumn dataField="INV_31" headerText="Ch Inv" width="50"/>
<mx:DataGridColumn dataField="ORD_31" headerText="Ch Ord" width="50"/>
<mx:DataGridColumn dataField="DAT_31" headerText="Ch Due" width="50"/>
<mx:DataGridColumn dataField="INT_31" headerText="Ch InT" width="50"/>
<mx:DataGridColumn dataField="INV_31" headerText="MH Inv" width="50"/>
<mx:DataGridColumn dataField="ORD_31" headerText="MH Ord" width="50"/>
<mx:DataGridColumn dataField="DAT_31" headerText="MH Due" width="50"/>
<mx:DataGridColumn dataField="INT_31" headerText="MH InT" width="50"/>
</mx:Array>
</mx:columns>
</mx:DataGrid>
</mx:VBox>
</mx:Accordion>
</mx:Panel>
</mx:Application>
What is the name of the remoteObject that you are using?
Is there a sample EXACTALY like the one provided, using the remoteObject instead of the ArrayCollection? I know the remoteObject needs to be called out into an ArrayObject, but how?
Thanks
Here is a full working example. To get this to work you only need to change path to the cfc in the remoteObject, and create and populate the table to query against:
Table Name: tblEmployee
Fields firstName varChar(),lastName varChar(),status varChar() ,currentStatus int
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="initApp()" height="460" width="800">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.ListEvent;
import mx.utils.ObjectUtil;
[Bindable]
public var empArray:ArrayCollection;
]]>
</mx:Script>
<mx:Script>
<![CDATA[
// On startup
public function initApp():void
{
exampleCFC.getEmpList();
}
private function getEmpListResult(event:Object):void
{
empArray = event.result as ArrayCollection;
empArray.filterFunction=processFilter;
empArray.refresh();
}
// Filter function
public function processFilter(item:Object):Boolean
{
var result:Boolean=false;
var pattern:RegExp = new RegExp("^"+txtFilter.text,"i");
// If no filter text, or a match, then true
if ((!item.lastName.length && ObjectUtil.toString(item.currentStatus) == ObjectUtil.toString(Status.selectedValue)) || (ObjectUtil.toString(item.currentStatus) == ObjectUtil.toString(Status.selectedValue) && item.lastName.match(pattern)))
result=true;
return result;
}
public function showFault(fault:mx.rpc.Fault):void
{
mx.controls.Alert.show(fault.faultString +' '+ fault.faultDetail, 'Fault Alert Box', mx.controls.Alert.OK);
}
]]>
</mx:Script>
<mx:RemoteObject
id="exampleCFC"
destination="ColdFusion"
source="path.to.cfc"
showBusyCursor="true">
<mx:method name="getEmpList" fault="showFault(event.fault)" result="getEmpListResult(event)" />
</mx:RemoteObject>
<mx:Panel width="596" height="334" layout="absolute" horizontalCenter="-38.5" verticalCenter="0" title="Search Filter Example">
<mx:VBox>
<mx:HBox width="500">
<mx:Label text="Employee Last Name:"/>
<mx:TextInput id="txtFilter" width="203" change="empArray.refresh();"/>
<mx:RadioButtonGroup id="Status" change="empArray.refresh();"/>
<mx:RadioButton label="Active" groupName="Status" value='1' selected="true"/>
<mx:RadioButton label="Inactive" groupName="Status" value='0'/>
</mx:HBox>
<mx:DataGrid dataProvider="{empArray}" width="501" height="263" x="10" y="77" id="employeeList">
<mx:columns>
<mx:DataGridColumn headerText="Last Name" dataField="lastName" width="200"/>
<mx:DataGridColumn headerText="First Name" dataField="firstName" width="150"/>
<mx:DataGridColumn headerText="Emp. ID" dataField="employeeID" width="100"/>
<mx:DataGridColumn headerText="Status" dataField="status" width="100"/>
<mx:DataGridColumn headerText="StatusID" dataField="currentStatus" width="100"/>
</mx:columns>
</mx:DataGrid>
</mx:VBox>
</mx:Panel>
</mx:Application>
HERE IS THE CFC THAT IS CALLED:
<cfcomponent hint="Model For Filter Example" output="false">
<cffunction name="getEmpList" returntype="Query" access="remote">
<cfquery name="getEmployeeList" datasource="#application.ds#">
SELECT e.employeeID,e.firstName,e.lastName,e.status,e.currentStatus
FROM tblEmployee e
ORDER BY e.LastName,e.FirstName
</cfquery>
<cfreturn getEmployeeList>
</cffunction>
</cfcomponent>
Thanks alot, Gus your sample is a BIG help. I want to add a comboBox to the filter to choose the column to filter on. This is what I have but it does not work:
<mx:Script>
<![CDATA[
public function initApp():void
{
}
public function processFilter(item:Object):Boolean
{
var result:Boolean=false;
var pattern:RegExp= new RegExp("^"+txtFilter.text,"i");
// this is where I put the column header name like pnum5w instead of selected item (then it works for that column)
if (!item.selectedItem.data.length
|| item.selectedItem.data.match(pattern))
result=true;
return result;
}
]]>
</mx:Script>
<mx:Script>
<![CDATA[
//I want the data to represent the column like the first item, I think
public var myfilter: Array = [ {label:"Product ID", data:'PNUM5W'},
{label:"Description", data:2}, {label:"Size", data:3}, {label:"Pack", data:4} ];
public var selectedItem:Object;
]]>
</mx:Script>
<mx:Label text="Filter:"/>
<mx:TextInput width="100" id="txtFilter" change="myData.refresh()" />
<mx:ComboBox dataProvider="{myfilter}" width="125"
close="selectedItem=ComboBox(event.target).selectedItem"/>
<mx:DataGrid dataProvider="{myData}" initialize="qResult.Inventory()" width="100%" height="100%">
<mx:columns>
<mx:DataGridColumn dataField="PNUM5W" headerText="Prod ID" width="55"/>
<mx:DataGridColumn dataField="PDES5W" headerText="Desc" width="163"/>
<mx:DataGridColumn dataField="SDIVDE" headerText="Size" width="50"/>
<mx:DataGridColumn dataField="ISCF3W" headerText="Pac" width="35"/>
<mx:DataGridColumn dataField="DIVN5W" headerText="Rnk" width="35"/>
... there is more
Anythoughts
Thanks
George
Where does the data. notation come from?
---------------------------------------------------------------------------------------
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="catalogService.send()" viewSourceURL="srcview/index.html">
<mx:Script>
<![CDATA[
private function filterProduct(item:Object):Boolean
{
return item.name.match(new RegExp("^" + brandText.text, "i"));
}
]]>
</mx:Script>
<mx:HTTPService id="catalogService" url="http://127.0.0.1/scratch/Flex/FlexDemo/catalog.xml..."
resultFormat="e4x" />
<mx:XMLListCollection id="xlc"
source="{catalogService.lastResult.*}"
filterFunction="filterProduct" />
<mx:Panel title="Product Catalog" width="380" height="385">
<!-- Begin Tile Code -->
<mx:TileList dataProvider="{xlc}" width="100%" height="300"
columnCount="2" rowCount="2">
<mx:itemRenderer>
<mx:Component>
<mx:VBox paddingLeft="30" width="150" height="150"
horizontalScrollPolicy="off" verticalScrollPolicy="off">
<mx:Label text="{data.name}" />
<mx:Label text="{data.price}"/>
<mx:Label text="{data.brand}"/>
</mx:VBox>
</mx:Component>
</mx:itemRenderer>
</mx:TileList>
<!-- End Tile Code -->
<!-- Begin Grid Code -->
<!-- <mx:DataGrid dataProvider="{xlc}">
<mx:columns>
<mx:DataGridColumn dataField="{data.name}" headerText="Name"/>
<mx:DataGridColumn dataField="{data.price}" headerText="Price"/>
<mx:DataGridColumn dataField="{data.brand}" headerText="Brand"/>
</mx:DataGridColumn>
</mx:columns>
</mx:DataGrid>
-->
<!-- End Grid Code -->
<mx:ControlBar>
<mx:Label text="Brand" />
<mx:TextInput id="brandText" width="75"
change="xlc.refresh()" />
</mx:ControlBar>
</mx:Panel>
</mx:Application>
I keep getting stymied by components. I've been trying to get this to work in an example where the remote object is called in a main mxml document but the data grid and text input for the filtered text is in another component.
I finally got it to work by adding the initialization function to a button. Not very elegant, but nothing else I've tried is working (e.g., attaching it to a creationComplete in the component)
What would be the best way to make this initialize without the user having to start it?
Thanks,
Al
I am trying to make this example work using a remote object as well. I know that I have to bind the data from the remote object to an ArrayCollection and then bind my datagrid to the ArrayCollection. I can not get it to work. Can someone please take a look.
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="initApp()">
<!-- This tag calls the java class phonebook which is aliased in the remoting-config.xml file.
It is set to an id of pb to be referenced in this app.-->
<mx:RemoteObject id="pb" destination="phonebook" showBusyCursor="true" />
<!-- End java class call tag -->
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.ListEvent;
import mx.utils.ObjectUtil;
// On startup
public function initApp():void
{
// Set filter function
// Be careful to set filterFunction only after ArrayCollection has been populated.
empData.filterFunction=processFilter;
}
// Filter function
public function processFilter(item:Object):Boolean
{
var result:Boolean=false;
// If no filter text, or a match, then true
if (!item.name.length
|| item.name.toUpperCase().indexOf(txtFilter.text.toUpperCase()) >= 0)
result=true;
return result;
}
]]>
</mx:Script>
<!-- Data (use ArrayCollection) -->
<mx:ArrayCollection id="empData" source="{pb.getPhoneBook.lastResult}" />
<!-- End Data (use ArrayCollection) -->
<!-- UI -->
<mx:HBox width="100%">
<mx:Label text="Filter:" />
<mx:TextInput id="txtFilter" width="100%" change="empData.refresh()" />
</mx:HBox>
<mx:DataGrid id="employeeView" dataProvider="{empData}"
initialize="pb.getPhoneBook()" height="100%" width="100%">
<mx:columns>
<mx:DataGridColumn dataField="fname" headerText="First Name" />
<mx:DataGridColumn dataField="lname" headerText="Last Name" />
<mx:DataGridColumn dataField="org" headerText="Organization" />
<mx:DataGridColumn dataField="telephone1" headerText="Telephone" />
</mx:columns>
</mx:DataGrid>
</mx:Application>
A Flex newbie
Daniel
I got this working after some hard work. Thanks for all the great examples above. I would like to ask how to get a combo box to filter rather then a text field. Here is my current working code. I would like to change Organization into a combo box.
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="initApp()">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.ListEvent;
import mx.utils.ObjectUtil;
[Bindable]
public var empArray:ArrayCollection;
]]>
</mx:Script>
<mx:Script>
<![CDATA[
// On startup get the data from pb
public function initApp():void
{
pb.getPhoneBook();
}
// Cast a variable called empArray to hold the ArrayCollection
private function getPhoneBookResult(event:Object):void
{
empArray = event.result as ArrayCollection;
empArray.filterFunction=processFilter;
empArray.refresh();
}
// Filter function for search fields
public function processFilter(item:Object):Boolean
{
var result:Boolean=false;
// If no filter text, or a match, then true
if (!item.lname.length
|| item.lname.toUpperCase().indexOf(lNameFilter.text.toUpperCase()) >= 0)
// If no filter text, or a match, then true
if (!item.fname.length
|| item.fname.toUpperCase().indexOf(fNameFilter.text.toUpperCase()) >= 0)
// If no filter text, or a match, then true
if (!item.org.length
|| item.org.toUpperCase().indexOf(orgFilter.text.toUpperCase()) >= 0)
result=true;
return result;
}
public function showFault(fault:mx.rpc.Fault):void
{
mx.controls.Alert.show(fault.faultString +' '+ fault.faultDetail, 'Fault Alert Box', mx.controls.Alert.OK);
}
]]>
</mx:Script>
<!-- This tag calls the java class phonebook which is aliased in the remoting-config.xml file.
It is set to an id of pb to be referenced in this app.-->
<mx:RemoteObject id="pb" destination="phonebook" showBusyCursor="true">
<mx:method name="getPhoneBook" fault="showFault(event.fault)" result="getPhoneBookResult(event)" />
</mx:RemoteObject>
<!-- End java class call tag -->
<!-- UI -->
<mx:HBox width="100%">
<mx:Label text="Last Name:" />
<mx:TextInput id="lNameFilter" width="100%" change="empArray.refresh()" />
<mx:Label text="First Name:" />
<mx:TextInput id="fNameFilter" width="100%" change="empArray.refresh()" />
<mx:Label text="Organization:" />
<mx:TextInput id="orgFilter" width="100%" change="empArray.refresh()" />
</mx:HBox>
<mx:DataGrid id="employeeView" dataProvider="{empArray}" height="100%" width="100%">
<mx:columns>
<mx:DataGridColumn dataField="fname" headerText="First Name" />
<mx:DataGridColumn dataField="lname" headerText="Last Name" />
<mx:DataGridColumn dataField="org" headerText="Organization" />
<mx:DataGridColumn dataField="telephone1" headerText="Telephone" />
</mx:columns>
</mx:DataGrid>
</mx:Application>
Thanks
Daniel
A Flex Newbie
I have two Lists
1.Teams
2.Team Member
I want to create a webpart which display the Team neme colomn from teams list and Who is the team leader for that particular team from Team Member list as another Colomn called Team Leader.
PLease give me the solution for this
Regards
sridebi
in script block (there are several 'joint' arrays and 1 'type' array):
//type comboBox
private var TYPE:Array=
[{label:"All", data:"all"},
{label:"Stretching", data:"stretching"},
{label:"Strengthening", data:"strengthening"},
{label:"Mobilising", data:"mobilising"},
{label:"Stabilising", data:"stabilising"},
{label:"Other", data:"other"}];
//JOINT ComboBox Data
private var UPPER_LIMB:Array=
[{label:"All", data:"all"},
{label:"Shoulder/Scapula", data:"shoudler scapula"},
{label:"Elbow", data:"elbow"},
{label:"Wrist", data:"wrist"},
{label:"Hand", data:"hand"},
{label:"Fingers", data:"fingers"}];
public function filterKeyword(item:Object):Boolean{
var result:Boolean=false;
if ((jointCat.selectedLabel.toUpperCase() == "All" || item.joint.toUpperCase() ==
jointCat.selectedLabel.toUpperCase()) && (typeCat.selectedLabel == "All" ||
item.exType == typeCat.selectedLabel) && (txtFilter.length == 0 ||
item.exKeywords.toUpperCase().indexOf(txtFilter.text.toUpperCase()) >=0 )){
result=true;
}
return result;
sortName();
}
The comboBoxes have ids 'jointCat' and 'typeCat'. I have tried jointCat.selectedItem.label and jointCat.selectedItem.data. Neither work.
However, I have found that the filter works is I select an option from one ComboBox and then click a dataGrid column heading which just displays a selected dataGrid item.
private function exDataGridClick():void{
if (exDataGrid.selectedItem){
this.currentState='exSelected';
}
}
Is there something simple I'm missing or do I just need a good nights sleep?
Thanks for any help,
Ben.
Ben.
I look at all the posts and sample above and still can't get my to work correctly. I know some where hee you need to create or use arraycollection. But I am just not get where with it. Please help review my scripts and point out what I did wrong.
Here are the script:
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*"
xmlns:cfComponents="com.adobe.ColdFusion.components.*"
creationComplete="initComponent()" width="210" height="400" cornerRadius="2">
<mx:Metadata>
[Event(name="addItem", type="com.adobe.windowedApplication.events.SelectionEvent")]
[Event(name="editItem", type="com.adobe.windowedApplication.events.SelectionEvent")]
</mx:Metadata>
<mx:Script>
<![CDATA[
import com.adobe.windowedApplication.managers.WindowManager;
import mx.core.UIComponent;
import mx.collections.ArrayCollection;
import mx.utils.ObjectUtil;
import mx.controls.Alert;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import com.adobe.windowedApplication.events.SelectionEvent;
private var currentIndex:int = 0;
[Bindable]
public var CompanyArray:ArrayCollection;
private var _key:Object;
[Bindable]
public function get key():Object
{
return this._key;
}
public function set key(key:Object):void
{
this._key = key;
}
private function initComponent():void
{
refreshList(null);
getCompanyResult(null);
}
public function refreshList(event:Event):void
{
this.dataManager.getMasterQuery(this.key);
}
private function deleteItem(key:Object):void
{
this.dataManager.deleteItem(key);
}
private function EditItem(key:Object):void
{
var e:SelectionEvent;
if( key == null )
{
e = new SelectionEvent("addItem", key);
}
else
{
e = new SelectionEvent("editItem", key);
}
this.dispatchEvent(e);
}
/**
* RemoteObject result and error handlers
*/
private function server_fault(event:FaultEvent):void
{
// dump error message
Alert.show( ObjectUtil.toString(event.fault) );
}
private function getMasterQuery_result(event:ResultEvent):void
{
//Alert.show( ObjectUtil.toString(event.result) );
this.masterList.dataProvider = event.result as ArrayCollection;
this.masterList.selectedIndex = this.currentIndex;
if (this.masterList.selectedItem != null)
{
this.EditItem( this.masterList.selectedItem.CompanyID );
}
}
private function deleteItem_result(event:ResultEvent):void
{
//Alert.show( ObjectUtil.toString(event.result) );
refreshList(null);
}
// Filter function
// Cast a variable called empArray to hold the ArrayCollection
private function getCompanyResult(event:Object):void
{
CompanyArray = event.result as ArrayCollection;
CompanyArray.filterFunction=processFilter;
CompanyArray.refresh();
}
// Filter function for search fields
public function processFilter(item:Object):Boolean
{
var result:Boolean=false;
// If no filter text, or a match, then true
if (!item.CompanyName.length
|| item.CompanyName.toUpperCase().indexOf(search.text.toUpperCase()) >= 0)
result=true;
return result;
}
]]>
</mx:Script>
<mx:RemoteObject
id="dataManager"
showBusyCursor="true"
destination="ColdFusion" source="TCI.components.cfgenerated.Companies">
<mx:method name="getMasterQuery" result="getMasterQuery_result(event)" fault="server_fault(event)" />
<mx:method name="deleteItem" result="deleteItem_result(event)" fault="server_fault(event)" />
</mx:R