Populate ArrayCollection with HTTPService - apache-flex

Yes there is a question like this one, but there is no activity and no answer.
I want to load data from a external XML file, using a HTTPService, and on the ResultEvent of the same HTTPService i want him to populate a ArrayCollection with the data from the XML.
I think a ArrayCollection is the ideal for this XML. but I'm open to suggestions.
XML
<?xml version="1.0" encoding="utf-8"?>
<PhotoGalleryData>
<Photo>
<id>1</id>
<name>Summer Vacation</name>
<description>In vacation</description>
<source>vacation.JPG</source>
</Photo>
<Photo>
<id>2</id>
<name>Winter Vacation</name>
<description>coold</description>
<source>vacation2.JPG</source>
</Photo>
</PhotoGalleryData>
I thought that this simple line in getDataResultHandler(), would be enough to populate the ArrayCollection.
<mx:HTTPService id="getData"
url="{XMLDataFileLocation}"
fault="getDataFaultHandler()"
result="getDataResultHandler()"/>
[Bindable]
private var PhotoData:ArrayCollection;
private function getDataResultHandler():void
{
PhotoData = new ArrayCollection(getData.lastResult.PhotoGalleryData.photo)
}
But i guess it isn't, because just to be sure i have placed a List binded to the ArrayCollection to actually see if it really was populated.
<mx:List dataProvider="{PhotoData}" labelField="name"/>
And the list didn't showed any data, so isn't working as supposed to be.
Thanks for any help.
EDIT
NOTE
The <mx:List/> used is just to be
sure that the ArrayCollection is
indeed populated, it won't be used in
the App.
Results taking Bozho advice.
With Bozho changes Flex doesn't report the var type error any more, but once i run it. Adobe flash does report this.
TypeError: Error #1034: Type Coercion
failed: cannot convert
mx.utils::ObjectProxy#22cd311 to
mx.collections.ArrayCollection. at
PhotoGallery/getDataResultHandler()[C:\Users\Fábio
Antunes\Documents\Flex Builder 3\Photo
Gallery\src\ActionScripts\PhotoGallery.as:56]
at
PhotoGallery/__getData_result()[C:\Users\Fábio
Antunes\Documents\Flex Builder 3\Photo
Gallery\src\PhotoGallery.mxml:23] at
flash.events::EventDispatcher/dispatchEventFunction()
at
flash.events::EventDispatcher/dispatchEvent()
at
mx.rpc.http.mxml::HTTPService/http://www.adobe.com/2006/flex/mx/internal::dispatchRpcEvent()[C:\autobuild\3.2.0\frameworks\projects\rpc\src\mx\rpc\http\mxml\HTTPService.as:290]
at
mx.rpc::AbstractInvoker/http://www.adobe.com/2006/flex/mx/internal::resultHandler()[C:\autobuild\3.2.0\frameworks\projects\rpc\src\mx\rpc\AbstractInvoker.as:193]
at
mx.rpc::Responder/result()[C:\autobuild\3.2.0\frameworks\projects\rpc\src\mx\rpc\Responder.as:43]
at
mx.rpc::AsyncRequest/acknowledge()[C:\autobuild\3.2.0\frameworks\projects\rpc\src\mx\rpc\AsyncRequest.as:74]
at
DirectHTTPMessageResponder/completeHandler()[C:\autobuild\3.2.0\frameworks\projects\rpc\src\mx\messaging\channels\DirectHTTPChannel.as:403]
at
flash.events::EventDispatcher/dispatchEventFunction()
Well and the line 23 that Flash reports the error its:
PhotoData = ArrayCollection(event.result);
Line 23 is:
result="getDataResultHandler(event)"

If you can use an XMLListCollection in place of an ArrayCollection the process of converting the result object is more straightforward. Here is a good tutorial explaining how to go about this.
EDIT:
The key things to get from this tutorial are this:
you need to set the result format of the service to e4x.
you need to cast the result object as XML object, extract the repeating nodes as an XMLList, and construct an XMLListCollection from the list like so:
private function httpService_result(evt:ResultEvent):void
{
var xmlList:XMLList = XML(evt.result).path.to.repeating.element;
xmlListColl = new XMLListCollection(xmlList);
}

You can simplify your script like this:
<mx:HTTPService id="getData" url="{XMLDataFileLocation}"/>
<mx:List dataProvider="{getData.lastResult.Photo}" labelField="name"/>
The lastResult of your getData will be the root of your XML. By retrieving lastResult.Photo you'll get an XMLList of the photos.

Related

Converting array of ObjectProxy objects to custom objects

I have a service which returns an Array of ObjectProxy objects. I would like to cast this to a custom object (a value object) and create an ArrayCollection. How can I do this?
Edited:
I am using Django and PyAMF for the backend. I had to write a custom SQL query and I am wrapping the resulting records in ObjectProxy and sending the whole result as an ArrayCollection.
Here is my client side code:
[ArrayElementType("SessionVO")]
[Bindable]
private var _list:ArrayCollection;
private function onSessionResultSuccess(event:ResultEvent):void
{
_list = new ArrayCollection(event.result as Array));
}
When I debug, I notice that the elements of event.result are of type ObjectProxy but the _list variable is null. Is there another than to loop over event.result and copy them into _list as SessionVO objects?
If you use the [RemoteClass] tag on your value objects, Flex remoting (Blaze, LCDS) will convert them to your value object for you when sending/returning from a remote service call.
The syntax for RemoteClass is
[RemoteClass(alias="com.co.custom.remote.class")] <--- point to the remote java/php class def
Public Class FooBar
{
public instance variable;
}
Your service can then return an array or hashtable of this class and Flex remoting will convert it for you.
There are cases where objects can become opaque, which you may need to create ObjectProxy code to do custom marshaling, but this is not common. The RemoteClass marshaling can handle very ccomplex object types, subtypes, embedded objects in objects, etc. As long as all of objects on the AS side are typed with RemoteClass, it works as expected.
I'm not sure what you mean by "ObjectProxy objects." I sounds to me like you are already getting returned an array of custom objects.
I recommend looking into some form of AMF gateway. Most serer side languages have an AMF add-on piece to them. It is built into ColdFusion, BlazeDS, and LiveCycle. PHP has ZendAMF and AMFPHP. .NET has FlourineFX and WebORB. Those are just a few examples.
AMF Gateways have a automatic conversion facility; so that the server side object can easily map to a client side object. Here is some info on the RemoteObject tag that describes this. Basically, you specify the RemoteClass metadata on your client side object, and usually specify some form of mapping on the server side object. The AMF Gateway magically handles the rest of the conversion.
In your RemoteObject result handler, you'll just have to convert the returned array to an ArrayCollection. Usually something like this:
var resultArray : Array = event.result as Array;
var resultCollection : ArrayCollection = new ArrayCollection(resultArray);
If you have no control over the server side piece of this application, you may be stuck looping over the results and manually converting them into client side Flex objects.
You can use com.adobe.serializers.utility.TypeUtility;
public function result_handler(event:ResultEvent):void{
var result:Array = TypeUtility.convertListToStrongType(event.result,YourValueObject) as Array;
}
make makeObjectsBindable="false" in the web service property, it will return object.
<s:WebService id="ws" wsdl="http://`xxxx/mobiledata/MobileDataService.asmx?wsdl" fault="fault(event)">
<s:operation
name="GetAll"
resultFormat="object"
result="GetData(event)" makeObjectsBindable="false"
/>
<s:operation
name="Create"
resultFormat="object"
result="SaveData(event)"
/>
</s:WebService>

Flex: dataProvider variable named "result" makes trouble. Why?

<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
private var result : ArrayCollection = new ArrayCollection([1,2,3]);
]]>
</mx:Script>
<mx:List dataProvider="{result}"/>
</mx:Application>
I have this code. The problem is: if my variable for dataProvider is named "result", then in the running application the List contains the only element "[object Binding]". By if I rename "result" for anything else (for example "res"), the List is displayed as expected - "1", "2", "3". Why?
Short answer: THIS IS A BUG IN FLEX. I HAVE REPORTED IT.
This is odd... If we use the Spark List control, it won't even compile. It tells us that it can't convert Array to IList. result is obviously a variable some place, but where?
So I looked into the code that is generated using the -keep-generated-actionscript=true compiler flag.
Inside the ViewName-generate.as file, you will find an interesting method:
private function _ViewName_bindingsSetup():Array
{
var result:Array = [];
result[0] = new mx.binding.Binding(this,
function():Object
{
return (result);
},
null,
"_ViewName_List1.dataProvider"
);
return result;
}
This is where the Binding objects are making into your result variable.
We can see in the binding object that there is a function that returns (result). In any other case, this would be something else like (results). BUT, in this case, it is returning the local array of Binding objects. That is why this.result works. It is pulling out of the local scope!
So, this is obviously a bug. I have submitted it to Adobe as such: https://bugs.adobe.com/jira/browse/FB-29870
I am just experimenting with Lists and Arrays in Flex. I tried this.result, it worked fine. I assume the result is maybe reserved.
Rob

flex data provider not working if XML has single node value or less

i get this error when i retrieve an XML that only has 1 node (no repeating nodes) and i try to store in an ArrayCollection. -When I have MORE than 1 "name" nodes...i do NOT get an error. My test show that XMLListCollection does NOT work either.
TypeError: Error #1034: Type Coercion failed: cannot convert "XXXXXX" to mx.collections.ArrayCollection.
this error occurs as the line of code:
myList= e.result.list.name;
Why can't ArrayCollection work with a single node? I'm using this ArrayCollection as a dataprovider for a Component -is there an alternative I can use that will take BOTH single and repeating nodes as well as work as a dataprovider? Thanks in advance!
code:
[Bindable]
private var myList:ArrayCollection= new ArrayCollection();
private function getList(e:Event):void{
var getStudyLoungesService:HTTPService = new HTTPService();
getStuffService.url = "website.com/asdf.php";
getStuffService.addEventListener(ResultEvent.RESULT, onGetList);
getStuffService.send();
}
private function onGetList(e:ResultEvent):void{
myList= e.result.list.name;
}
the problem here is, that if you only have one row Flex will complain since it cannot use the result as arrayCollection.
My work around was, that you put number of rows into your XML with the data you want to return:
for example I did:
<list><nr_rows>3</nr_rows><name>...</name><name>...</name><name>...</name></list>
So when I get the result back I do a check of how many rows I am getting with (You can get the number of rows returned from MySQL query with mysql_num_rows)
e.result.list.nr_rows
So if this is one, you add Object to the arrayCollection, if there are more than one you can just use result and equal it to AC (in this case projects is AC):
if (event.result.list.nr_rows == '1'){
myList.addItem(event.result.list.name);
} else {
myList = event.result.list.name;
}
If you are not particular about getting the final result onto a ArrayCollection, you can do the following to get the final result onto an XMLList or XMLListCollection.
1) set the resultFormat property of HTTPService to e4x.
2) Do not mention the root xml tag name while referencing it. Reference the output as: myList:XMLList = e.result.name.
This works whether the retrieved XML has one or more than one elements.
I just had this problem today and it led me to this question. I'm not sure if your use-case is exactly the same as mine, but you might want to try showRoot="true" on the mx:Tree. It seems to force the root node to be shown when there is only one item, and gets ignored with multiple items.
add a row object to get the row of the XML node can work, but I think some better method have be, is ActionScript is as powerful as Java?

Injecting an object created by OjbectBuilder as a property to view

I have a PresentationModel AS class that holds all the values used in SomeView.mxml. The entire class for the model is bindable, and the model property in the view is also bindable. However, I am unable to inject the model into the view using the PropertyInjector tag:
- INFO: Data binding will not be able to detect assignments to model
Would someone familier with Flex data binding and Mate give me a hand? Thanks a lot!
MainEventMap.mxml
<EventHandlers type="{FlexEvent.INITIALIZE}">
<ObjectBuilder generator="{PresentationModel}" registerTarget="true">
<Properties dispatcher="{scope.dispatcher}"/>
</ObjectBuilder>
</EventHandlers>
<Injectors target="{SomeView}" debug="true">
<PropertyInjector targetKey="model" source="{PresentationModel}" />
</Injectors>
Snippet from PresentationModel.as
[Bindable]
public class PresentationModel extends EventDispatcher
{
public var dispatcher:IEventDispatcher;
//.....other variables and functions
}
Snippet from SomeView.mxml
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="518" height="562" >
<mx:Script>
<![CDATA[
//...... all the imports
[Bindable]
public var model:OSGiBrokerConsoleModel;
// ......other variables and functions
]]>
</mx:Script>
// ..... actual view components
</mx:Canvas>
You can safely ignore that information message.
That message is usually shown when you have a PropetyInjector with source and source key, where the property defined by "sourceKey" is not bindable, so we want to make sure you know that the current value of that property will be the only one the target will ever get (when the property is not bindable, the value is copied and no binding is established). That may or may not be what you want.
In this case, there is no sourceKey, because you don't want to bind to any specific property of the source. Instead, you want to pass the whole PM to the view. Because of that, you don't want to establish a binding, just send the value to the view once.
In the cases where there is no sourceKey or when you are simply sending a one-off value (ie: when you are sending a constant), the message can be ignored.
You cannot bind to a class. Making a class bindable means all members of that class will be bindable, but not the definition itself.
You should create a member function(getter/setter) for the presentation model that returns the data you want to use as the source. Then you also need to create an instance of PresentationModel that you can use for the binding. So rather than binding to PresentationModel.data, you'd bind to myPM.data.

flex doesn't seem to bind with custom actionscript object

I have a custom actionscript object defined as bindable with a number of public properties.
[Bindable]
public class MyObject extends Object {
public var mobileNumber:String;
...
In my mxml I have:
<mx:Script><![CDATA[
import mx.binding.utils.BindingUtils;
import org.test.MyObject;
[Bindable]
private var obj: MyObject = new MyObject();
]]></mx:Script>
<mx:Label text="Mobile Number" id="mobileNumberLabel"/>
<mx:TextInput id="mobileNumberText" text="{obj.mobileNumber}" />
<mx:LinkButton label="Load" id="loadButton" enabled="true" click="obj = obj.load();"/>
<mx:LinkButton label="Save" id="saveButton" enabled="true" click="obj.write();"/>
My issue is that when I enter a new value in the field for mobile number and then click the save button, the value typed is not logged out... i.e.:
public function write():void {
var bytes:ByteArray = new ByteArray();
trace("write - mobile:" + this.mobileNumber);
bytes.writeObject(this);
EncryptedLocalStore.setItem(KEY, bytes);
}
I also tried adding in:
private function init():void {
BindingUtils.bindProperty(mobileNumberText, "text", obj, "mobileNumber");
}
but no luck with that either.
I'm probably missing something simple here, but not sure what it is. Hope you can help, thanks.
tst's answer is correct - bindings are one-way. I'm guessing you already knew that though, since you tried to setup the reverse binding in your init() method.
However, there's two problems with your init() method.
First, it's not clear where you put that init() method, or what calls it.
Second, you got the method parameters backwards.
What I typically do in situations like this is either use the mxml tag as the first responder suggested, or if I'm in AS3 code, I do something like this:
private function onCreationComplete(event:Event):void
{
BindingUtils.bindProperty(obj, "mobileNumber", mobileNumberText, ["text"]);
}
Note a couple of points here:
1/ BindingUtils.bindProperty() is "left hand side = right hand side". Thus, it's kinda like writing
obj.mobileNumber = mobileNumberText.text;
Or, closer to what is "actually going on" inside the binding classes:
obj["mobileNumber"] = mobileNumberText["text"];
2/ BindingUtils.bindProperty() actually wants an array as the last param. This is so that you can do "chained properties" logically like:
obj.mobileNumber = mobileNumbersGrid.selectedItem.text;
which would be
BindingUtils.bindProperty(obj, "mobileNumber", mobileNumbersGrid,
["selectedItem", "text"]);
3/ Best practice tip: if you're binding a property chain whose initial member is a property of yourself (this), then write it as:
BindingUtils.bindProperty(obj, "mobileNumber", this,
["mobileNumbersGrid", "selectedItem", "text"]);
This neatly handles the case where your "right hand side object" this.mobileNumbersGrid instance itself is replaced with a new instance object.
4/ If you ever reassign obj ("the left hand side"), you need to create a new binding to the new obj instance. Typically, you do this by turning the local obj property into a getter/setter pair like this:
public function get obj():MyObject
{
return _obj;
}
private var _obj:MyObject = new MyObject();
public function set obj(value:MyObject):void
{
_obj = value;
BindingUtils.bindProperty(_obj, "mobileNumber", mobileNumberText, ["text"]);
}
(Note: to be really careful, you'd stash the returned value from BindingUtils.bindProperty() which is a ChangeWatcher instance, and you'd unwatch() to prevent the old object from receiving property changes)
Hope this helps. Bindings are among the most powerful tools in the Flex framework, but can cause a lot of headaches when used poorly. In particular, be aware of memory leaks and "unexpected updates" when bindings are left "hanging around".
This code:
<mx:TextInput id="mobileNumberText" text="{obj.mobileNumber}" />
makes a one-directional binding between obj.mobileNumber and mobileNumberText.text. What you need is an additional reverse binding - add the following line to your code and it will work:
<mx:Binding source="mobileNumberText.text" destination="obj.mobileNumber"/>
Did you try implementing IEventDispatcher, or extending EventDispatcher?
Bindings work through dispatching of PropertyChangeEvents, so maybe this is the problem?
also, I am not sure, whether bindings work on variables, or whether they require properties (getters/setters).
You should try compiling with -keep-generated-actionscript and see if the resulting code makes any sense, i.e. dispatches any events, if the variable is accessed ...

Resources