Cannot bind data to DropDownList control in Flex 4 - apache-flex

I'm fetching some data from a PHP application using Zend AMF. However I can't get the data to bind to a simple DropDownList control. The PHP method is:
class Test
{
public function myMethod()
{
$res = array();
$res[] = array('NAME' => 'ThisIsATest', 'ID' => 1);
return $res;
}
}
Network Monitor reports the method is returning results. It's returning the following as an array:
Array
(
[0] => Array
(
[NAME] => Property
[ID] => 1
)
)
Below is the Flex code:
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="500" height="286"
creationComplete="initApp()">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
private function myMethodResult(e:ResultEvent):void
{
searchType.dataProvider = e.result as ArrayCollection;
}
protected function initApp():void
{
service.myMethod();
}
protected function faultHandler(event:FaultEvent):void
{
trace(event.fault.faultString);
}
]]>
</fx:Script>
<fx:Declarations>
<s:RemoteObject id="service"
destination="zend"
source="Test"
showBusyCursor="true"
fault="faultHandler(event)">
<s:method name="myMethod" result="myMethodResult(event)"/>
</s:RemoteObject>
</fx:Declarations>
<s:DropDownList id="searchType" labelField="NAME"/>
</s:WindowedApplication>
Any help would be greatly appreciated. Thanks in advance.

You ask about binding, but I don't think that's what you want to know about. I believe the answer is this line in the result handler:
searchType.dataProvider = e.result as ArrayCollection;
I'm assuming that you are getting back an Array from ColdFusion. If memory serves me, you cannot cast an array as an ArrayCollection. The result will, most likely, be null. Have you stepped through code in debug mode to verify?
Instead try this:
searchType.dataProvider = new ArrayCollecection(e.result as Array);
Since e.result is a generic object, you'll need to cast it as an array.
To address the binding portion of your answer. Binding has a source and a value. When the source changes, the value is automatically updated. You have a value ( dropDownList.dataProvider ) that you want to change, but you do not have a source for that. Nothing in your code makes use of binding. You're just manually setting the value when the results come back. To make use of binding I might modify your code like this:
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="500" height="286"
creationComplete="initApp()">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
// create a variable taht can be used as the source for a binding operation
[Bindable]
public var mySource : ArrayCollection;
private function myMethodResult(e:ResultEvent):void
{
// searchType.dataProvider = e.result as ArrayCollection;
// change the value of your binding source
mySource = new ArrayCollection(e.result);
}
protected function initApp():void
{
service.myMethod();
}
protected function faultHandler(event:FaultEvent):void
{
trace(event.fault.faultString);
}
]]>
</fx:Script>
<fx:Declarations>
<s:RemoteObject id="service"
destination="zend"
source="Test"
showBusyCursor="true"
fault="faultHandler(event)">
<s:method name="myMethod" result="myMethodResult(event)"/>
</s:RemoteObject>
</fx:Declarations>
<!-- and finally, specify your dataProvider as the target for binding -->
<s:DropDownList id="searchType" labelField="NAME" dataProvider="{this.mySource }"/>
</s:WindowedApplication>
I wrote all code in a browser and it may not be "compile perfect"

#Flextras
searchType.dataProvider = new ArrayCollecection(e.result);
...resulted in...
1118: Implicit coercion of a value with static type Object to a possibly unrelated type Array.
Instead I tried...
searchType = ArrayCollection(e.result);
But this resulted in...
Error #1034: Type Coercion failed: cannot convert []#812a1c9 to mx.collections.ArrayCollection
Then I tried...
typeArray.source = e.result as Array;
...and...
<s:DropDownList labelField="NAME">
<s:ArrayCollection id="typeArray"/>
</s:DropDownList>
This works! \o/

Related

Flex: DataGrid column formatting of numbers

I'm trying to format some numbers in a column of a DataGrid. I'm getting an error in my simplified test program below when I run it. All the examples I've seen so far have column data that are strings. Is there a way to do it using numbers? How to modify the code below to format the checking values?
<?xml version="1.0" encoding="utf-8"?>
<mx: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">
<fx:Script>
<![CDATA[
[Bindable]
public var checking:Array = new Array(1000000.2222, 0, 1000);
private function myLabelFunction(item:Array, column:DataGridColumn):String {
var result:String;
result = myFormatter.format(item);
return result;
}
]]>
</fx:Script>
<fx:Declarations>
<s:NumberFormatter id="myFormatter"
fractionalDigits="2"
decimalSeparator="."
groupingSeparator=","
useGrouping="true"
negativeNumberFormat="0"
/>
</fx:Declarations>
<mx:DataGrid id="dg1" dataProvider="{checking}" >
<mx:columns>
<mx:DataGridColumn dataField="checking" headerText="Checking"
labelFunction="myLabelFunction" />
</mx:columns>
</mx:DataGrid>
</mx:Application>
Change filter function signature (item should be Object)
private function myLabelFunction(item:Object, column:DataGridColumn):String
Remove dataField="checking" from column.
While the label function will certainly work -- I usually prefer an ItemRenderer for things like this. You override the render function and then you can display whatever it is in the grid view "box" however you like.
A decent example is here. Scroll down about 1/4 the way down for a DataGrid example.
In case of object you must use/No caso de objeto, deve-se usar:
private function myLabelFunction(item:Object, column:GridColumn):String {
var result:String;
result = myFormatter.format(item[column.dataField]);
return result;
}

function parameter with FLEX code

I have a code in MXML and ActionScript that I found in a Flex manual. The problem is with "val" variable that should be passed to the updateMyString() function calling statement as a parameter but it doesn't happen in the code. Why is that?
<?xml version="1.0"?>
<!-- binding/BindSetterAS.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">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
import mx.binding.utils.*;
import mx.events.FlexEvent;
// Method called when myTI.text changes.
public function updateMyString(val:String):void {
myTA.text = val.toUpperCase();
}
<!-- Event listener to configure binding. -->
public function mySetterBinding(event:FlexEvent):void {
var watcherSetter:ChangeWatcher =
BindingUtils.bindSetter(updateMyString, myTI, "text");
}
]]>
</fx:Script>
<s:Label text="Bind Setter using setter method"/>
<s:TextInput id="myTI"
text="Hello Setter" />
<s:TextArea id="myTA"
initialize="mySetterBinding(event);"/>
</s:Application>
The documentation says that BindingUtils.bindSetter expects a setter function with one argument:
setter:Function — Setter method to invoke with an argument of the current value of chain when that value changes
In this case the val parameter will be set to the current value of chain. You can read about it here: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/binding/utils/BindingUtils.html#bindSetter
Hope this helps!
var watcherSetter:ChangeWatcher = BindingUtils.bindSetter(updateMyString, myTI, "text");
the function is like this:
bindSetter(functionToCall, objectToBind, PropertyToBind)
So, you are actually binding myTI.text. This is passed as an argument to functionToCall, i.e. updateMyString. Got it?

Data Grid not displaying data in array collection

My data grid is displaying stale data, rather than the real time data available in it's data provider (array collection). I've tried refeshing the data in the collection, but that has no effect. Below is my code, does anyone see what could be the problem?
<mx:Accordion/>
<fx:Script>
<![CDATA[
private var _gridData:ArrayCollecton = new ArrayCollection;
[Bindable] public function get gridData():ArrayCollection { return _gridData; }
public function set gridData(value:ArrayCollection):void { _gridData = value; }
public function loadGridData():void {
// imgCollection is the data returned from the server
var tempCollection:ArrayCollection = new ArrayCollection();
for (var i:int = 0; i < imgCollection.length; i++)
{
var img:Object = new Object();
img.id = imgCollection.getItemAt(i).imgId;
img.url = "http://..." + imgCollection.getItemAt(i).imgId;
img.caption = (imgCollection.getItemAt(i).imgCaption == null) ? "": imgCollection.getItemAt(i).imgCaption;
img.group = images;
tempCollection.addItem(new ObjectProxy(img));
}
gridData = tempCollection;
<!-- Use http service to get data and save it in grid data array collection, this is run on accordion create completion and whenever data is added or removed from the array collection -->
}
]]>
</fx:Script>
<!-- NOTE: There is a cyclic binding between the data grid and the gridData array collection -->
<fx:Binding source="dg.dataProvider as ArrayCollection" destination="gridData"/>
...
...
<s:NavigatorContent>
<s:Panel>
<mx:DataGrid dataProvider="{gridData}" ...>
...
...
</mx:DataGrid>
</s:Panel>
</s:NavigatorContent>
UPDATE:
I tried the suggestions mentioned below, however, they do not resolve the issue. The data grid has custom item renderers, could that be the problem?
<?xml version="1.0" encoding="utf-8"?>
<s:MXDataGridItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
focusEnabled="true">
<mx:Image id="image" source="{data.url}" height="65" maintainAspectRatio="true" scaleContent="true"/>
</s:MXDataGridItemRenderer>
You don't need "cyclical" binding, because in your datagrid you do not change the collection, but you change its items. The collection stays intact. the dataprovider of the DataGrid and your _gridData point to the same collection.
If I'm not mistaking you should have [Bindable] on the setter as well because the datagrid has not other way of knowing when your data has changed.
Regards, Alin
Looks to me like you are overthinking this. Since you aren't doing anything in your getter/setters you could get rid of them and just mark your ArrayCollection as Bindable, then set it as the dataProvider for the DataGrid and be done:
<fx:Script>
<![CDATA[
[Bindable]
public var gridData:ArrayCollecton = new ArrayCollection;
public function loadGridData():void {
// Whenever you change the gridData, the DataGrid will update appropriately.
}
]]>
</fx:Script>
<mx:DataGrid dataProvider="{gridData}"></mx:DataGrid>
The problem with your existing code is likely that you are not dispatching a change event in your setter. Getting rid of the getter/setters allows the ArrayCollection to handle dispatching that event for you. Hope that helps.
EDIT: Based on the updated question, you may want to try changing your renderer to look like this, which would help if your custom data object is not bindable.
<?xml version="1.0" encoding="utf-8"?>
<s:MXDataGridItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
focusEnabled="true">
<fx:Script>
<![CDATA[
override public function set data(value:Object):void {
super.data = value;
image.source = value.url;
}
]]>
</fx:Script>
<mx:Image id="image" source="{data.url}" height="65" maintainAspectRatio="true" scaleContent="true"/>

flex builder: how to populate an array from an external file of strings

hello i'm new to flex builder and trying to populate an array from an external file consisting of a list of strings.
how do i go about that? should i use some sort of a data object?
Here's an example to get you started:
Sample File (file_with_strings.txt):
one, two, three
Sample App
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
initialize="initializeHandler()">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
protected function initializeHandler():void
{
service.send();
}
protected function updateList(result:Object):void
{
var array:Array = result.split(/,\s+/);
var collection:ArrayCollection = new ArrayCollection(array);
list.dataProvider = collection;
}
]]>
</mx:Script>
<mx:HTTPService id="service"
url="file_with_strings.txt"
resultFormat="text" result="updateList(event.result)"/>
<mx:List id="list"/>
</mx:Application>
I would just use the HTTPService class to load your external file. You can change the resultFormat to XML, Object, and a few other things if you'd like. Then just customize that updateList() method however.
Hope that helps,
Lance

How do I ensure a Flex dataProvider processes the data synchronously?

I am using an component, and currently have a dataProvider working that is an ArrayCollection (have a separate question about how to make this an XML file... but I digress).
Variable declaration looks like this:
[Bindable]
private var _dpImageList : ArrayCollection = new ArrayCollection([
{"location" : "path/to/image1.jpg"},
{"location" : "path/to/image2.jpg"},
{"location" : "path/to/image3.jpg"}
]);
I then refer to like this:
<s:List
id="lstImages"
width="100%"
dataProvider="{_dpImageList}"
itemRenderer="path.to.render.ImageRenderer"
skinClass="path.to.skins.ListSkin"
>
<s:layout>
<s:HorizontalLayout gap="2" />
</s:layout>
</s:List>
Currently, it would appear that each item is processed asynchronously.
However, I want them to be processed synchronously.
Reason: I am displaying a list of images, and I want the leftmost one rendered first, followed by the one to its right, and so on.
Edit:
I just found this answer.
Do you think that could be the same issue?
Instead of declaring the variable and using it as the binding source, declare two collections. Then, onCreationComplete call loadNext() which shifts an object out of the second array and pushes it into the first. When the item has been rendered (custom event dispatched by itemRenderer and caught) call loadNext() again until such time as your source array is empty and your bound dataProvider has all the images.
I can write it in code if this doesn't make any sense. ;)
<?xml version="1.0" encoding="utf-8"?>
<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/halo" minWidth="1024" minHeight="768" creationComplete="init()">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
private var _source : ArrayCollection = new ArrayCollection([
{"location" : "path/to/image1.jpg"},
{"location" : "path/to/image2.jpg"},
{"location" : "path/to/image3.jpg"}
]);
[Bindable] private var dataProvider:ArrayCollection;
protected function init():void
{
this.lstImages.addEventListener( "imageLoaded", handleImageLoaded);
loadImage()
}
protected function loadImage():void
{
if(this._source.length<=0)
return;
var image:Object = this._source.getItemAt(0);
dataProvider.addItem(image);
this._source.removeItemAt(0);
}
protected function handleImageLoaded(event:Event):void
{
loadImage()
}
]]>
</fx:Script>
<s:List
id="lstImages"
width="100%"
dataProvider="{_dpImageList}"
itemRenderer="path.to.render.ImageRenderer"
skinClass="path.to.skins.ListSkin"
>
<s:layout>
<s:HorizontalLayout gap="2" />
</s:layout>
</s:List>
</s:Application>
Your item renderer's image's complete handle will dispatch:
protected function handleImageLoaded(event:Event):void
{
owner.dispatch(new Event("imageLoaded"));
}
And that should load your images in a clean sequence.

Resources