What can be used instead of tree.dataProvider in adobe flex? - apache-flex

I have my .mxml as below:
<fx:Declarations>
<fx:XMLList id="data">
<node>
<node label="Inbox">
<node label="Marketing"/>
<node label="Product Management"/>
<node label="Personal"/>
</node>
<node label="Outbox">
<node label="Professional"/>
<node label="Private"/>
</node>
<node label="Spam">kushal</node>
<node label="Sent"/>
</node>
</fx:XMLList>
</fx:Declarations>
<mx:VBox>
<mx:Button label="Search by name" click="findByName()" />
<mx:Tree id="tree" width="500" height="500"
showRoot="false" dataProvider="{data}"
labelField="#label" />
</mx:VBox>
I'm trying to click findByName() on click of a button:
which is:
private function findByName():void
{
var mixml:XMLList = new XMLList(data);
var searchStr:String = "Outbox";
//child.expandChildrenOf(myXML[0], false);
//mixml=data;
searchResult= mixml.node.(#label==searchStr);
var xn:XML = searchResult[searchResultIndex];
Alert.show("xn"+ xn);
searchResultIndex = 0;
if (searchResult[searchResultIndex] != undefined)
var xmlNode:XML = searchResult[searchResultIndex];
while (xmlNode.parent() != null) {
Alert.show("xmlNodeBefore"+ xmlNode);
xmlNode = xmlNode.parent();
Alert.show("xmlNodeAfter"+ xmlNode);
//checkpoint 1
tree.expandItem(xmlNode, true, false);
tree.selectedItem = searchResult[searchResultIndex];
Alert.show(" tree.selectedItem " + tree.selectedItem );
}
}
If here instead of data
I use tree.dataProvider then this code doesn't work, can somebody tell me why?

Your code does not work when you use tree.dataProvider because it's of the type XMLListCollection, which is a wrapper on top of the XMLList object. Flex internally transforms the XMLList into a XMLListCollection, because XMLListCollection has capabilities of providing change notifications via events, which XMLList does not.
So in your code, if you use tree.dataProvider at the following line :
var mixml:XMLList = new XMLList(tree.dataProvider);
You are actually trying to type cast and convert an XMLListCollection into a XMLList, which is why it does not work.
Interestingly, the XMLListCollection has public properties (like most collections in Flex) which can be used to access the underlying XMLList source from which it is made up of. So, to access the data from the tree, one correct way would be:
var mixml:XMLList = new XMLList(tree.dataProvider.source);
I have tried this in your code and it seems to do the same job as your above code.
You can read more about this here :
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/collections/XMLListCollection.html#source
Hope this explanation works and it answers your question overall.
Cheers.

If you check out the setter for Tree.dataProvider you can see that it modifies/translates the value that get's passed in.
This line of code will cast to null because dataProvider changes xml based data into XmlListCollection.
var mixml:XMLList = new XMLList(data);
Here is the setter for reference.
override public function set dataProvider(value:Object):void
{
// in all cases save off the original
if (_rootModel)
_rootModel.removeEventListener(
CollectionEvent.COLLECTION_CHANGE,
collectionChangeHandler);
// handle strings and xml
if (typeof(value)=="string")
value = new XML(value);
else if (value is XMLNode)
value = new XML(XMLNode(value).toString());
else if (value is XMLList)
value = new XMLListCollection(value as XMLList);
if (value is XML)
{
_hasRoot = true;
var xl:XMLList = new XMLList();
xl += value;
_rootModel = new XMLListCollection(xl);
}
//if already a collection dont make new one
else if (value is ICollectionView)
{
_rootModel = ICollectionView(value);
if (_rootModel.length == 1)
_hasRoot = true;
}
else if (value is Array)
{
_rootModel = new ArrayCollection(value as Array);
}
//all other types get wrapped in an ArrayCollection
else if (value is Object)
{
_hasRoot = true;
// convert to an array containing this one item
var tmp:Array = [];
tmp.push(value);
_rootModel = new ArrayCollection(tmp);
}
else
{
_rootModel = new ArrayCollection();
}
//flag for processing in commitProps
dataProviderChanged = true;
invalidateProperties();
}

Related

Flex: Accessing SQLite entry using 'getItemAt()'

I have an array populated with input from an SQLite database. When I use this array as the dataprovider for a s:list then it will display all the objects in the array that I specify.
I would like to access some of the objects in the array, and have been trying to use getItemAt(). At the moment I can return the first object in the array, but changing the index (to getItemAt(1) for example), it returns nothing.
Ultimately I am aiming to populate an FX:Model with data entered elsewhere in the application and inserted into the SQLite database. The model then acts as the dataprovider for a pie chart. I can pass the first entry of the database on to the model/pie chart, but can't access others.
Some relevant sections of code are as follows. Trouble shooting tips would be appreciated.
[Bindable]
private var GHGsource:ArrayCollection;
And then:
GHGsource = new ArrayCollection();
}
for each ( var o:Object in event.data )
{
GHGsource.addItem(o);
}
FullList.dataProvider = GHGsource;
}
}
Model setup:
<fx:Declarations>
<fx:Model id="GHG" >
<data>
<row>
<input>Enteric methane</input>
<Value> {GHGsource.getItemAt(0).answer} </Value>
List setup:
<s:List id="FullList">
<s:itemRenderer>
<fx:Component>
<s:IconItemRenderer labelFunction="returnQuestion" messageFunction="returnAnswer">
<fx:Script>
<![CDATA[
private function returnQuestion(item:Object):String
{
return "Question: " + item.question;
}
private function returnAnswer(item:Object):String
{
var response:String = "";
if ( !item.answer || item.answer == "" )
{
response = "(no response)";
} else {
response = item.answer;
}
return response;
}
]]>
</fx:Script>
</s:IconItemRenderer>
</fx:Component>
</s:itemRenderer>
</s:List>
This application is based on the database structures set out in Daniel Koestler's survey ape application.
Perhaps an insight into how the s:List component accesses objects in an array will help here?
Additional detail:
Running in debug mode it seems as though that the objects are not binding correctly to the arraycollection.
warning: unable to bind to property 'xxx' on class 'Object' (class is not an IEventDispatcher)
I have tried to follow guides on the following links, but have not been sucessfull.
link1
link2
Ok, got it.
GHGsource = new ArrayCollection();
}
for each ( var o:Object in event.data )
{
GHGsource.addItem(o);
}
FullList.dataProvider = GHGsource;
}
}
Becomes:
[Bindable]
private var passingArray:Array;
And:
GHGsource = new ArrayCollection();
passingArray = new Array();
}
for each ( var o:Object in event.data )
{
GHGsource.addItem(o);
passingArray[o] = new ObjectProxy(event.data[o]);
}
// not needed as the list is no longer needed - FullList.dataProvider = GHGsource;
}
}
Then this works:
<fx:Declarations>
<fx:Model id="GHG" >
<data>
<row>
<input>Enteric methane</input>
<Value> {GHGsource.getItemAt(3).answer} </Value>

AdvancedDataGrid dataField - how to use a subarray or object in flex?

I have an advanced data grid in flex (flash builder 4). It's dataProvider is pointing to an ArrayCollection (this._encounters).
Inside that array collection is a property that is an object (a client object).
I tried setting the dataField to "clientObj.firstName" to refer to the first name property within the clientObj property of the this._encounters array collection. It did not show anything.
So, I added a labelFunction to that column (code below) to set the text in the cell. This works fine and now I have values showing in the grid.
The problem is now when I click the title of column to sort it. It throws an error that property clientObj.firstName is not found in my array collection!
So, is there a better way to set the dataField / source for a column and point at a property in a sub-object -- or a way to fix the sort?
Below the first column
<mx:AdvancedDataGrid x="0" y="25" id="adgEncounters" designViewDataType="flat" width="100%" height="100%" dataProvider="{this._encounters}">
<mx:columns>
<mx:AdvancedDataGridColumn headerText="first" dataField="clientObj.firstName" labelFunction="encounterGridLabelFunct"/>
<mx:AdvancedDataGridColumn headerText="first" dataField="thisWorksField"/>
</mx:columns>
</mx:AdvancedDataGrid>
protected function encounterGridLabelFunct(item:Object, column:AdvancedDataGridColumn):String //put just the HH:MM in to the grid, not the whole date string
{
if(column.headerText=="first") result=item.clientObj.firstName;
return result;
}
update:
Here is the final working code I used. 3 example sort functions, 1 for numeric sorting, 1 for String sorting and one for Date sorting (string date from a database).:
// sort adg column numerically
private function numericSortByField(subObjectName:String, fieldName:String):Function
{
return function(obj1:Object, obj2:Object):int
{
var value1:Number = (obj1[subObjectName][fieldName] == '' || obj1[subObjectName][fieldName] == null) ? null : new Number(obj1[subObjectName][fieldName]);
var value2:Number = (obj2[subObjectName][fieldName] == '' || obj2[subObjectName][fieldName] == null) ? null : new Number(obj2[subObjectName][fieldName]);
return ObjectUtil.numericCompare(value1, value2);
}
}
//sort adg column string
private function stringSortByField(subObjectName:String, fieldName:String):Function
{
return function(obj1:Object, obj2:Object):int
{
var value1:String = (obj1[subObjectName][fieldName] == '' || obj1[subObjectName][fieldName] == null) ? null : new String(obj1[subObjectName][fieldName]);
var value2:String = (obj2[subObjectName][fieldName] == '' || obj2[subObjectName][fieldName] == null) ? null : new String(obj2[subObjectName][fieldName]);
return ObjectUtil.stringCompare(value1, value2);
}
}
//sort adg date diff (takes date strings for example from a db)
private function dateSortByField(subObjectName:String, fieldName:String):Function
{
return function(obj1:Object, obj2:Object):int
{
var value1:String = (obj1[subObjectName][fieldName] == '' || obj1[subObjectName][fieldName] == null) ? null : new String(obj1[subObjectName][fieldName]);
var value2:String = (obj2[subObjectName][fieldName] == '' || obj2[subObjectName][fieldName] == null) ? null : new String(obj2[subObjectName][fieldName]);
var value1Date:Date = new Date();
var value1Time:int = value1Date.setTime(Date.parse(value1));
var value2Date:Date = new Date();
var value2Time:int = value2Date.setTime(Date.parse(value2));
return ObjectUtil.numericCompare(value1Time, value2Time);
}
}
In the mxml above, this is the changed line - notice the stringSortByField added:
<mx:AdvancedDataGridColumn headerText="first" dataField="clientObj.firstName" sortCompareFunction="{stringSortByField('clientObj','firstName')}" labelFunction="encounterGridLabelFunct"/>
If it were a numeric field use the numericSortByField. If it were a date string from a database, use the dateSortByField function instead.
You could use a custom compare function, thats how I do it (obviously your itemA
and itemB will be different objects, so cast them as such):
In your AdvancedDataGridColumn put in:
sortCompareFunction="string_sortCompareFunc"
and make the function:
private function string_sortCompareFunc(itemA:Object, itemB:Object):int
{
var a:String = itemA as String;
var b:String = itemB as String;
return ObjectUtil.stringCompare(a, b);
}

Compiler warns me that binding will not work but why I run the application it does work!

The Flex application below generates the compiler warning: Data binding will not be able to detect assignments to 'dp'. This seems correct since the variable 'dp' is not a bindable property (there is no [Bindable] metadata tag). I have added a button which appends items to the back of 'dp' when it is clicked. Although the compiler warns me that I will not see changes to 'dp', the list shows the new item every time the button is clicked!
I do not understand why I can see new items appear in the list. Can someone explain why this still works although 'dp' is not bindable?
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" minWidth="955" minHeight="600">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.rpc.events.ResultEvent;
private var arrayData:Array = [
{name:"banana", cat:"fruit", cost:0.99},
{name:"bread", cat:"bakery", cost:1.99},
{name:"orange", cat:"fruit", cost:0.52},
{name:"donut", cat:"bakery", cost:0.33},
{name:"apple", cat:"fruit", cost:1.05}];
private var dp:ArrayCollection = new ArrayCollection(arrayData);
private function onButtonClick(event:MouseEvent):void
{
var obj:Object = new Object();
obj.name="test";
obj.cat="testcat";
obj.cost=666;
dp.addItem(obj);
}
]]>
</mx:Script>
<mx:HorizontalList dataProvider="{dp}" labelField="name" columnWidth="100" width="80%" height="50"/>
<mx:Button label="Click me" click="onButtonClick(event)" />
The compiler is correct in it's warning.
The compiler is warning you that assignments that change the value of dp from the initial ArrayCollection you specified to another ArrayCollection won't be detected.
However, if you leave the value of dp alone, and only change the contents of it, then your <HorizontalList /> will continue to work.
This may seem trivial, but it's an important distinction, and one that can lead to some very confusing bugs further down the road in your application.
Assignments to the variable dp will not be detected. However, changes to the ArrayCollections list will, because they dispatch a CollectionChangeEvent.
eg:
private var dp:ArrayCollection = new ArrayCollection();
private function test():void
{
// Here, we don't change the value of dp directly,
// instead we just modify it's list.
// The DataGroup will show the strings One,Two
dp.addItem("One")
dp.addItem("Two")
// Here, we change the actual value of dp, by assigning a
// new ArrayCollection to it.
// This change would not be detected, and the list would continue to show
// the contents of the previous value.
// Additionally, the label will show the string "Length: 2",
// even though the length is clearly now 3.
dp = new ArrayCollection();
dp.addItem("Tahi");
dp.addItem("Rua");
dp.addItem("Toru");
}
<s:DataGroup dataProvider="{dp}" />
<s:Label text="Length: {dp.length}" />
Try to use:
[Bindable("__NoChangeEvent__")]
private var dp:ArrayCollection = new ArrayCollection(arrayData);
What about adding elements in list see the code of ListBase:
public function set dataProvider(value:Object):void
{
if (collection)
{
collection.removeEventListener(CollectionEvent.COLLECTION_CHANGE, collectionChangeHandler);
}
if (value is Array)
{
collection = new ArrayCollection(value as Array);
}
else if (value is ICollectionView)
{
collection = ICollectionView(value);
}
else if (value is IList)
{
collection = new ListCollectionView(IList(value));
}
else if (value is XMLList)
{
collection = new XMLListCollection(value as XMLList);
}
else if (value is XML)
{
var xl:XMLList = new XMLList();
xl += value;
collection = new XMLListCollection(xl);
}
else
{
// convert it to an array containing this one item
var tmp:Array = [];
if (value != null)
tmp.push(value);
collection = new ArrayCollection(tmp);
}
// get an iterator for the displaying rows. The CollectionView's
// main iterator is left unchanged so folks can use old DataSelector
// methods if they want to
iterator = collection.createCursor();
collectionIterator = collection.createCursor(); //IViewCursor(collection);
// trace("ListBase added change listener");
collection.addEventListener(CollectionEvent.COLLECTION_CHANGE, collectionChangeHandler, false, 0, true);
clearSelectionData();
var event:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE);
event.kind = CollectionEventKind.RESET;
collectionChangeHandler(event);
dispatchEvent(event);
itemsNeedMeasurement = true;
invalidateProperties();
invalidateSize();
invalidateDisplayList();
}
So take a look at line:
collection.addEventListener(CollectionEvent.COLLECTION_CHANGE, collectionChangeHandler, false, 0, true);

Populate comboBox in flex 4 using remote Object

I have a remote object returned and I am trying to populate it into combobox.
<s:RemoteObject id="ro" result="result(event)" destination="echoServiceDestination">
private var statesData:ArrayCollection;
private function result(e:ResultEvent):void{
statesData = e.result as ArrayCollection;
}
How can I turn this collection into something like {label:"Red", data:"#FF0000"} so that I can populate into combobox
The remote object is party and I can't seem to able to cast it as below
var party:Party = new Party;
for(var i:int = 0 ; i < statesData.length; i++)
{
party = statesData.getItemAt(i);
}
Thanks for the help.
You need to implement a label function. Set the returning data directly as dataprovider to your combobox:
<mx:ComboBox id="comboBox"
dataProvider="{statesData}"
labelFunction="labelFunc" />
This will be your label function:
private function labelFunc(item:Object):String {
return item.label; // Or whatever parameter you want to display
}
]]>
</mx:Script>

How to access the array elements

var count:uint = 0;
var textInputs:Array /* of TextInputs */ = [];
for(var i:String in columnsData){
textInputs[count] = new TextInput();
addChild(textInputs[count]);
count++;
}
here how can i access the first, second of Array of textinputs in the form of string or any thing to further proceed
I am not sure if I understood what your question is, but you're not setting the value for text inputs:
I prefer it the following way:
//make the array an instance variable instead of local var
//so that it can be accessed from other functions if required.
public var textInputs:Array = [];
for(var str:String in columnsData)
{
var ti:TextInput = new TextInput();
ti.text = str;
addChild(ti);
textInputs.push(ti);
}
You can access the values using textInputs[0].text etc.
if(textInput != null){
for(var i:Number = 0; i < textInputs.length; i++)
trace(textInputs[i].text);
}
(getChildAt(0) as TextInput).text //first
(getChildAt(1) as TextInput).text //second
If your sprite contains something other than TextInput at indices 0 & 1 you'll get null reference exception.
<?xml version="1.0"?>
<!-- dpcontrols\UseIList.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
initialize="initData();">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
import mx.collections.*;
// The data provider is an Array of Strings
public var myArray:Array = ["AZ", "MA", "MZ", "MN", "MO", "MS"];
// Declare an ArrayList that represents the Array.
[Bindable]
public var myAL:ArrayList;
//Initialize the ArrayList.
public function initData():void {
myAL = new ArrayList(myArray);
}
// The function to change the collection, and therefore
// the Array.
public function changeCollection():void {
// Get the original collection length.
var oldLength:int=myAL.length;
// Remove the invalid first item, AZ.
var removedItem:String=String(myAL.removeItemAt(0));
// Add ME as the second item. (ILists used 0-based indexing.)
myAL.addItemAt("ME", 1);
// Add MT at the end of the Array and collection.
myAL.addItem("MT");
// Change the third item from MZ to MI.
myAL.setItemAt("MI", 2);
// Get the updated collection length.
var newLength:int=myAL.length;
// Get the index of the item with the value ME.
var addedItemIndex:int=myAL.getItemIndex("ME");
// Get the fifth item in the collection.
var index4Item:String=String(myAL.getItemAt(4));
// Display the information in the TextArea control.
ta1.text="Start Length: " + oldLength + ". New Length: " +
newLength;
ta1.text+=".\nRemoved " + removedItem;
ta1.text+=".\nAdded ME at index " + addedItemIndex;
ta1.text+=".\nThe item at index 4 is " + index4Item + ".";
// Show that the base Array has been changed.
ta1.text+="\nThe base Array is: " + myArray.join();
}
]]>
</fx:Script>
<s:ComboBox id="myCB" dataProvider="{myAL}"/>
<s:TextArea id="ta1" height="75" width="300"/>
<s:Button label="rearrange list" click="changeCollection();"/>
</s:Application>

Resources