Populate comboBox in flex 4 using remote Object - apache-flex

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>

Related

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

Custom Component Not Updating Display List

I've created a component which is basically a meter (extends skinnable container). The skin to the component contains two rectangles (background and the actual meter). This component receives an arraycollection with the following fields value, max and min(the arraycollection I am passing is Bindable). When I initialize and pass the data to be used to draw It works great (keep in mind - only works the first time). I've created a button that changes the data array and shows the current meter value. Apparently the meter's value is changing everytime I modify the ArrayCollection I've set to be used for rendering.(dont want to say "dataProvider" because it is not a Flex dataprovider, just a variable )... here is the code...
public class meter extends SkinnableContainer {
[SkinPart( required = "true" )]
public var meter:spark.primitives.Rect;
[SkinPart( required = "true" )]
public var meterBackground:spark.primitives.Rect;
private var _dataProvider:ArrayCollection;
private var _renderDirty:Boolean = false;
public function Meter() {
super();
}
public function set dataProvider( value:Object ):void {
if ( value )
{
if(value is ArrayCollection)
{
_renderDirty = true;
_dataProvider = value as ArrayCollection;
}
if(_dataProvider)
{
_dataProvider.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, propertyChanged);//used both eventlisteners but none actually ever fire off
_dataProvider.addEventListener(CollectionEvent.COLLECTION_CHANGE,collectionChanged);
}
invalidateDisplayList();//only happens the first time
}
}
private function collectionChanged(event:CollectionEvent):void
{
Alert.show("In collection Change");//this never goes off when I change the "dataProvider"
_renderDirty = true;
invalidateDisplayList();
}
private function propertyChanged(event:PropertyChangeEvent):void
{
Alert.show("In property Change");//this never goes off when I change the "dataProvider"
_renderDirty=true;
invalidateDisplayList();
}
public function get dataProvider():Object {
return _dataProvider;
}
override protected function updateDisplayList( unscaledWidth:Number, unscaledHeight:Number ):void {
if(_dataProvider))
{
var span:Number = unscaledWidth / _dataProvider[0].max;
var meterWidth:Number = _dataProvider[0].value * span;
meter.width = meterWidth;
_renderDirty = false;
}
}
And this is the mxml code where I change the "value" field....
<fx:Script>
<![CDATA[
[Bindable]
private var meterData:ArrayCollection = new ArrayCollection([
{value:80, max:100, min:0}
]);
protected function mySlider_changeHandler(event:Event):void
{
meterData[0].value = Math.round(mySlider.value)*10;
}
protected function button1_clickHandler(event:MouseEvent):void
{
// TODO Auto-generated method stub
var array:ArrayCollection = testMeter.dataProvider as ArrayCollection;
var value:Number = array[0].value as Number;
Alert.show(value.toString());
// testMeter.meter.width= Math.random()*100;
}
]]>//initial value in "meterData" does get drawn...but when it's changed with the slider..nothing happens..
<custom:Meter id="testMeter" dataProvider="{meterData}" />
<s:HSlider id="mySlider"
liveDragging="true"
dataTipPrecision="0"
change="mySlider_changeHandler(event)"
value="3"/>
<s:Button click="button1_clickHandler(event)"/>
Can you help me figure out what's going on??Thanks guys!!!
The issue is that you are not resetting the dataProvider, nor doing anything to fire off a collectionChange event. I'll deal with each one individually. Here is how you reset the dataProvider:
testMeter.dataProvider = newDataPRovider;
But, you aren't doing that. So the set method will never execute after the first initial set.
If you need the collectionChange event to fire, you need to change the collection. You aren't actually doing that. You are changing the object in the collection. This code does not change the collection:
meterData[0].value = Math.round(mySlider.value)*10;
It just changes a single property in one of the objects of the collection. Try something like this:
var newObject = meterData[0];
newObject['value'] = Math.round(mySlider.value)*10
meterData.addItem(newObject);
This code should fire off the colletionChange event, even though the code you have does not.
I have a few more thoughts, unrelated to your primary question. Be sure to removeEventListeners in the dataProvider set method, like this:
public function set dataProvider( value:Object ):void {
if ( value )
{
// remove the event listeners here before changing the value
// that should allow the object to have no extra 'references' that prevent it from being garbage collected
_dataProvider.removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE, propertyChanged);
_dataProvider.removeEventListener(CollectionEvent.COLLECTION_CHANGE,collectionChanged);
if(value is ArrayCollection)
{
_renderDirty = true;
_dataProvider = value as ArrayCollection;
}
if(_dataProvider)
{
_dataProvider.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, propertyChanged);//used both eventlisteners but none actually ever fire off
_dataProvider.addEventListener(CollectionEvent.COLLECTION_CHANGE,collectionChanged);
}
invalidateDisplayList();//only happens the first time
}
}
And you said:
(dont want to say "dataProvider" because it is not a Flex
dataprovider, just a variable )
A dataProvider on the Flex List, or DataGrid, or DataGroup is also "just a variable." I don't see how the "Flex Framework" implementation is all that different than what you've done, conceptually anyway. Since you're specifically expecting a min and max value perhaps you should use a Value Object with those explicit properties instead of an ArrayCollection.

AS3, how to trigger a search function when item is selected in combobox?

I have a combobox that act as autosuggestion for a search application. Search function is getting triggered by a search button. I also want to trigger the search function either when the item in combobox is double or single clicked. Code:
//for triggering search function from combobox(search_complex) it will be
something like that but i am not sure
search_complex.addEventListener(Event.CHANGE, search);
search(event:Event):void{//something will come hereto use "selctedItem" to
trigger search function}
//search function which is working fine by pressing search button
bt_search.addEventListener(MouseEvent.CLICK, search);
function search(MouseEvent):void{
currentUserbase = [];
for (var n:int = 0; n<allUserbase.length; n++)
{
for (var k:int = 0; k<allUserbase[n].complex.length; k++)
{
if ((allUserbase[n].complex[k].value.toLowerCase() ==
search_complex.text.toLowerCase() || search_complex.text==""))
{
currentUserbase.push(allUserbase[n]);
}
}
}
updateList();
}//end search
I don't understand what you exactly want.
Is it rue that you have a search function, that will work fine.
Now you don't need for each event seperate handler. It is enough to use one for all events. As functionparameter use type "Event" because all other events inherit from this base class.
Check my Code. cd is my combobox. This example is written in flex3
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable] private var arr:ArrayCollection = new ArrayCollection([
{name:"Alexander"},
{name:"Bernd"},
{name:"Carl"}
]);
private function init():void
{
cb.addEventListener(MouseEvent.CLICK,search);
cb.addEventListener(MouseEvent.DOUBLE_CLICK,search);
cb.addEventListener(Event.CHANGE,search);
}
private function search (event:Event) :void
{
trace (event.type);
}
]]>
</mx:Script>
I believe you're on the right track. Try:
search_complex.addEventListener(Event.CHANGE, search);
bt_search.addEventListener(MouseEvent.CLICK, search);
function search(event:Event):void
{
currentUserbase = [];
for (var n:int = 0; n<allUserbase.length; n++)
{
for (var k:int = 0; k<allUserbase[n].complex.length; k++)
{
if ((allUserbase[n].complex[k].value.toLowerCase() == search_complex.text.toLowerCase() || search_complex.text==""))
{
currentUserbase.push(allUserbase[n]);
}
}
}
updateList();
}//end search
You should be able to get the selected item in your combobox using search_complex.selectedItem.label or search_complex.selectedItem.label depending on which property you need to use.

Extending Flex DataGridColumn for custom sorting function

I extended the DataGridColumn because I wanted to include a custom itemToLabel function (to be able to show nested data in the DataGrid. See this question.
Anyways, it also needs a custom sorting function. So I have written the sorting function like so:
private function mySortCompareFunction(obj1:Object, obj2:Object):int{
var currentData1:Object = obj1;
var currentData2:Object = obj2;
//some logic here to get the currentData if the object is nested.
if(currentData1 is int && currentData2 is int){
var int1:int = int(currentData1);
var int2:int = int(currentData2);
var result:int = (int1>int2)?-1:1;
return result;
}
//so on for string and date
}
And in the constructor of my CustomDataGridColumn, I've put:
super(columnName);
sortCompareFunction = mySortCompareFunction;
Whenever I try to sort the column, I get the error "Error: Find criteria must contain at least one sort field value."
When I debug and step through each step, I see that the first few times, the function is being called correctly, but towards the end, this error occurs.
Can someone please shed some light on what is happening here?
Thanks.
I have seen this error too, and I tracked it down to one of the cells containing 'null'.
And if I remember correctly, this error also shows up, when one of the columns has a wrong 'dataField' attribute.
hth,
Koen Weyn
Just to specify how exactly I solved this problem (for the benefit of others):
Instead of using the dataField property (to which I was assigning something like data.name, data.title since I was getting data from a nested object), I created my own field nestedDataField and made it bindable. So my code basically looks like this now:
public class DataGridColumnNested extends DataGridColumn{
[Bindable] public var nestedDataField:String;
private function mySortCompareFunction(obj1:Object, obj2:Object):int{
var currentData1:Object = obj1;
var currentData2:Object = obj2;
//some logic here to get the currentData if the object is nested.
if(currentData1 is int && currentData2 is int){
var int1:int = int(currentData1);
var int2:int = int(currentData2);
var result:int = (int1>int2)?-1:1;
return result;
}
//so on for string and date
}
}
Then I assign to this new variable my dataField entry
<custom:DataGridColumnNested headerText="Name" nestedDataField="data.name"/>
And lo and behold! it works without a hitch.
I often find it easier to use the standard datafield and just write a getter function in my valueobject to use as a datafield.
For example:
//file SomeObject.as with a nested object as property
public class SomeObject
{
public var someProperty:AnotherObject;
public function get someString():String;
{
if(someProperty)
return someProperty.someString;
else
return "";
}
}
//your nested class, AnotherObject.as
public class AnotherObject
{
public var someString:String;
}
//this way sorting and the label will work without custom label/compare functions
<mx:DataGridColumn headerText="" dataField="someString"/>
The easiest way to solve the problem is change the dataField="obj.atributte" by a labelFunction. If you want you can add a sortCompareFunction too.
<mx:DataGridColumn headerText="email"
dataField="user.email" textAlign="center" />
change by
<mx:DataGridColumn headerText="email"
labelFunction="emailLabelFunction"
sortCompareFunction="emailsCompareFunction2"
textAlign="center" />
public static function emailLabelFunction(item:Object, column:DataGridColumn):String{
if (item!=null){
var user:User = (item as TpAnnouncementUser).user as User;
return user.email;
}else{
return "";
}
}
public static function emailCompareFunction(obj1:Object, obj2:Object):int{
var dato1:String = User(obj1).email.toLowerCase();
var dato2:String = User(obj2).email.toLowerCase();
return ObjectUtil.compare(dato1, dato2);
}
public static function emailsCompareFunction2(obj1:Object, obj2:Object):int{
var dato3:User = (TpAnnouncementUser(obj1).user as User);
var dato4:User = (TpAnnouncementUser(obj2).user as User);
return emailCompareFunction(dato3, dato4);

Populating ArrayCollection with HTTPService

I am creating a RSS Feed application based on a data, and I have the following:
I have an ArrayCollection that is pre-populated with data. I am sorting through the ArrayCollection, get 1 piece of data (condition), and need to connect to an RSS feed which returns me the title, and I set my ArrayCollection in correspondence to condition -> title.
public function updateArrayList(list:ArrayCollection):ArrayCollection {
trace(list);
for(var i:int = 0; i < list.length; i++) {
// Alert.show(list.getItemAt(i).condition);
getRSSUpdate(list.getItemAt(i).condition);
list.getItemAt(i).title = getRSS.lastResult.article.title;
}
return list;
}
public function getRSSUpdate(condition:String):void {
getRSS = new HTTPService();
getRSSParam = new Object;
getRSSParam.condition = condition;
getRSS.method = "POST";
getRSS.url = "http://localhost/site/remoteRequests/flash/rss/getRSS.php";
getRSS.send(getRSSParam);
}
Basically, I want to iterate through the list ArrayCollection, and update list.getItemAt(i).title with result passed from the HTTPService.
This doesn't work! Help!
First make a result event on httpservice in that only you will access the result for the request.
In that method you will get resultEvent from that take out the required value if it return the response as xml you can directly do like this lastResult.article.title
<mx:HTTPService id="yahooHTTPService"
url="http://search.yahooapis.com/WebSearchService/V1/webSearch"
method="GET"
makeObjectsBindable="true" result="httpServiceResult(event)"
fault="httpServiceFault(event)" showBusyCursor="true">
</mx:HTTPService>
here is an example http://livedocs.adobe.com/flex/3/html/help.html?content=data_access_2.html#193905

Resources