Flex4: Using httpService in consecutive function calls - apache-flex

I have this button:
<s:Button includeIn="MeniuPrincipal" label="Descarcare Date" click="downloadLmData(event)"/>
and this click event handler:
protected function downloadLmData(event:MouseEvent):void
{
downloadData('competenta', 'competente');
downloadData('localitate', 'localitati');
}
the downloadData function looks like this:
private function downloadData(item:String, items:String):void
{
try {
var colVar:String = 'col' + cappitalize(items);
this.status = "Descarcare date in curs...";
this[colVar] = null;
var service:HTTPService = new HTTPService();
service.url = serverUrl + items + '/xml';
service.resultFormat = "xml";
service.method = "GET";
service.addEventListener(ResultEvent.RESULT, addArguments(downloadDataComplete, [item, items]));
service.send();
} catch (error:Error) {
errorHandler.defaultErrorHandler(error);
}
}
The problem is, all calls are ignored, except for the first one. Is there any "queuing" mechanism which would allow all calls to be made?
Thank you.

You need to chain your asynchronous calls. See these 2 blog posts for implementations :
http://kuwamoto.org/2006/05/16/dealing-with-asynchronous-events-part-1/
http://kuwamoto.org/2006/05/16/dealing-with-asynchronous-events-part-2/

I will rather use observer pattern. The easiest way.

Related

Flex applying the sort/filter on an arraycollection without dispatching event

I have a object that is extended from arraycollection. This object has to access and manipulate the arraycollections source object. When this happens, the local sorted/filter copy of data goes out of sync with the source data. To line things up correctly, the sort/filter needs to be re-applied.
To do this normally, you would call refresh() on the arraycollection, but this also broadcasts a refresh event. What I want is to update the sort/filter without dispatching an event.
Having looked into the ArrayCollection class, I can see it is extended from ListCollectionView. The refresh function
public function refresh():Boolean
{
return internalRefresh(true);
}
is in ListCollectionView and it calls this function
private function internalRefresh(dispatch:Boolean):Boolean
{
if (sort || filterFunction != null)
{
try
{
populateLocalIndex();
}
catch(pending:ItemPendingError)
{
pending.addResponder(new ItemResponder(
function(data:Object, token:Object = null):void
{
internalRefresh(dispatch);
},
function(info:Object, token:Object = null):void
{
//no-op
}));
return false;
}
if (filterFunction != null)
{
var tmp:Array = [];
var len:int = localIndex.length;
for (var i:int = 0; i < len; i++)
{
var item:Object = localIndex[i];
if (filterFunction(item))
{
tmp.push(item);
}
}
localIndex = tmp;
}
if (sort)
{
sort.sort(localIndex);
dispatch = true;
}
}
else if (localIndex)
{
localIndex = null;
}
revision++;
pendingUpdates = null;
if (dispatch)
{
var refreshEvent:CollectionEvent =
new CollectionEvent(CollectionEvent.COLLECTION_CHANGE);
refreshEvent.kind = CollectionEventKind.REFRESH;
dispatchEvent(refreshEvent);
}
return true;
}
annoyingly, that function is private and so is unavailable to and class that extends ListCollectionView. Also, a lot of what is in the internalRefresh function is private too.
Does anyone know of a way to call internalRefresh from a class that extends ArrayCollection? Or a way of stopping the refresh event from being dispatched when refresh is called?
My (read:hack) solution to this:
addEventListener(CollectionEventKind.REFRESH, handlerHack, true);
The true adds this listener onCapture, before anyone else gets to act on the event.
Before you call the collection.refresh() to update sort/filter, set a boolean flag to true.
discardRefreshEvent = true;
myCol.refresh();
In the listener...
private function handlerHack(evt:CollectionEvent):void
{
if (discardRefreshEvent)
{
evt.stopImmediatePropagation();
discardRefreshEvent = false;
}
}
Disclaimer: Haven't done this exact use before (have implemented similar functionality with other events), also only guessing on Event types/names.
maybe you could extend ArrayCollection, listen to the refresh event and call stopImmediatePropagation() on it when it is fired ? I would start with this...
Good luck :-)

Playing sound files one after another in flex AIR code

I have a set of sound clips to be played one after another in a sequence with couple of time interval inbetween.
In my case, its a question - followed by the set of four options.
When I write the below code, all the audop files start together at same time. How can I had time delay inbetween, so that the second clip plays only after the first one is over, and the third one starts playing, only when the second option is over.
I am with Flex AIR AS 3. See code below. Thanks in advance.
private function playCoundClips(): void
{
//set audio clips
var questionClipSource : String = "assets/quiz_voiceovers/" + questionCode + "Q.mp3";
var optionAClipSource : String = "assets/quiz_voiceovers/" + questionCode + "a.mp3";
var optionBClipSource : String = "assets/quiz_voiceovers/" + questionCode + "b.mp3";
var optionCClipSource : String = "assets/quiz_voiceovers/" + questionCode + "c.mp3";
var optionDClipSource : String = "assets/quiz_voiceovers/" + questionCode + "d.mp3";
playThisClip(questionClipSource);
playThisClip(optionAClipSource);
playThisClip(optionBClipSource);
playThisClip(optionCClipSource);
playThisClip(optionDClipSource);
}
private function playThisClip(clipPath : String) : void
{
try
{
clipPlayingNow = true;
var soundReq:URLRequest = new URLRequest(clipPath);
var sound:Sound = new Sound();
var soundControl:SoundChannel = new SoundChannel();
sound.load(soundReq);
soundControl = sound.play(0, 0);
}
catch(err: Error)
{
Alert.show(err.getStackTrace());
}
}
Thanks
Sumit
The problem is you are spawning multiple asynchronous calls. Implement a complete call back function on Sound and then call your playThisClip function inside the callback function. (You can sleep for predefined time before calling)
Time delay, is a very bad idea (in 99% of the case).
Have a look at the SOUND_COMPLETE event (see the doc)
This event is triggered when the sound stops playing.
So, it's now very easy to play sounds in sequence.
A simple example (not tested but idea is here) :
//declare somewhere a list of sounds to play
var sounds:Array=["sound_a.mp3","sound_a.mp3"];//sounds paths
//this function will play all sounds in the sounds parameter
function playSounds(sounds:Array):void{
if(!sounds || sounds.length==0){
//no more sound to play
//you could dispatch an event here
return;
}
var sound:Sound=new Sound();
sound.load(new URLRequest(sounds.pop()));
var soundChannel:SoundChannel = sound.play();
soundChannel.addEVentListener(Event.SOUND_COMPLETE,function():void{
soundChannel.removeEventListener(Event.SOUND_COMPLETE,arguments.callee);
playSounds(sounds);
});
}
This helped me
http://livedocs.adobe.com/flex/3/html/help.html?content=Working_with_Sound_09.html
One need to write code for:
sound.addEventListener(Event.ENTER_FRAME, onEnterFrame);
soundControl.addEventListener(Event.SOUND_COMPLETE, onPlaybackComplete);
private function onEnterFrame(event:Event):void
{
var estimatedLength:int =
Math.ceil(sound.length / (sound.bytesLoaded / sound.bytesTotal));
var playbackPercent:uint =
Math.round(100 * (soundControl.position / estimatedLength));
}
private function onPlaybackComplete(event:Event):void
{
Alert.show("Hello!");
}

Why is object property null?

Below is my class, which simply reads an xml file and provides the contents in e4x format. Unfortunately, after the constructors executes and sets the xmlProperties property with the expected values, it some how becomes null. Anyone know what I'm doing wrong?
public class WebService
{
private var _propertiesReader:HTTPService;
private var _xmlProperties:XML;
public function WebService()
{
_propertiesReader = new HTTPService();
_propertiesReader.url = "../resources/properties.xml";
_propertiesReader.resultFormat = "e4x";
_propertiesReader.contentType = "application/xml";
_propertiesReader.addEventListener(ResultEvent.RESULT, function(event:ResultEvent):void
{
_xmlProperties = XML(event.result);
});
_propertiesReader.addEventListener(FaultEvent.FAULT, function(event:FaultEvent):void
{
Alert.show("Unable to load properties content: " + event.fault.message + "\nPlease try again later.", "Properties File Load Error");
});
_propertiesReader.send();
}
public function get xmlProperties():XML
{
return _xmlProperties;
}
}
_xmlProperties is being set by a File Load call (via a callback event). It is not being set directly in the constructor.
Are you sure you are waiting for the call to finish and the callback event to fire before you check the value of _xmlProperty?

AS3/Flex 4: Most Practical Way To Find Nested Children

I'm sort of jumping in headfirst to some Flex/AIR stuff. I have a pretty solid background with AS3, but given the inherent hierarchal complexity of Flex (compared to regular Flash), I'm running into an issue.
Let's assume that you have an app where pretty much everything is event driven (common). Accessing elements in the near vicinity of the event target, or the event target itself, is trivial. I'm trying to find, however, the most practical (read: best, most efficient) way to find children that are far removed from the current context.
I know there are functions like getChildAt() and getChildByName(), but that assumes a parent context; what if the element (Flex) you're looking for is several parents up, in a sibling, and then several children down? We take for granted things like jQuery that do this easily, but obviously we don't have that luxury in AS3.
Are any of the following valid? Is there a better way?
Iterate through parents and parents' parents until you find a stop point, find the sibling, and iterate through children and their children until you find your target;
Keep key objects in a global object store (sic) and reference them as necessary (yech)
Use specific dot notation to reach the target, including elements (like skins and their containers - yech again)
Any thoughts would be appreciated.
Edit:
To clarify, let's take an empty Flex 4 AIR app. We have WindowedApplication as the root, obviously, and let's add two SkinnableContainer children with IDs navContainer and mainContainer, respectively. Both have custom skins. Within mainContainer, we have another SkinnableContainer with a vertical layout and ID mainContent, and as one of its children, it has an object (any will do - a spark BorderContainer, maybe) with the ID animatedBox, for example. Within the navContainer, we have a spark Button, which has a listener bound for MouseEvent.CLICK. Within that function, we are going to want to access animatedBox (nativeWindow.mainContainer.mainContent.animatedBox) and animate it to change, say, it's width.
The goal is to access that distant DisplayObject (animatedBox) in a way that is as unobtrusive and efficient as possible, while still conforming to Flex standards that I clearly have yet to possess. :)
in my implementation it is easy to do (however it's in pure AS3):
in display object which handles the click:
private function onClick(e:MouseEvent):void{
Radio.broadcast(new CustomEvent(id, ..params));
}
in animatedBox:
Radio.addListener(id, new Reciever(uid, animate));
private function animate(e:CustomEvent) {
//needed code and access of CustomEvent props you pass
}
upd:
package lazylib.broadcast
{
/**
* ...
* #author www0z0k
*/
public class Reciever
{
private var id: String;
private var toRun: Function;
/*#param nm - unique listener id - required
*#param fn - event handler function - required*/
public function Reciever(nm:String, fn:Function)
{
id = nm;
toRun = fn;
}
public function onEvent(e:* = null):String {
if (e == null) { return id; }
toRun(e);
return id;
}
public function get ID():String { return id; }
}
}
and
package lazylib.broadcast
{
import flash.events.Event;
import flash.events.EventDispatcher;
/**
* ...
* #author www0z0k
*/
public final class Radio extends EventDispatcher
{
private static var listeners: Object = new Object();
private static var archive: Array = new Array();
private static var forSlowpokes: Object = new Object();
public static function get ForSlowpokes():Object { return forSlowpokes; }
public static function addListener(type: String , listener: Reciever):Boolean {
listeners['anchor'] = null;
if (!listeners[type]) {
var o: Object = new Object();
listeners[type] = o;
}
if (!listeners[type][listener.ID]) {
listeners[type][listener.ID] = listener;
return true;
}else {
return false;
}
}
public static function broadcast(evt: * , singleUse:Boolean = false):void {
var type:String = (evt as Event).type;
if (listeners[type]) {
var returned: Array = new Array();
for (var i: String in listeners[type]) {
if(listeners[type][i]){
var fnRetVal: String = listeners[type][i].onEvent(evt);
returned.push(fnRetVal);
}else{
//trace("no listener for id = " + i + ' , type = ' + type);
}
}
}else {
//trace("nobody's interested in : \"" + type + "\"");
}
if (singleUse) {
forSlowpokes[type] = 'you missed it realtime';
delete listeners[type];
}
}
public static function clearDeadFuncs(namez:Object):void {
for (var a:String in namez) {
if (a != 'anchor') {
killListener(a, namez[a]);
}
}
}
public static function killListener(type: String , id: String):Boolean {
if (!listeners[type]) {
//trace("there are no listeners for event : " + "\"" + type + "\"");
return false;
}else {
if (!listeners[type][id]) {
//trace("there is no \"" + id + "\" listener for event : " + "\"" + type + "\"");
return false;
}else {
listeners[type][id] = null;
//trace("removed listener \"" + id + "\" for event : " + "\"" + type + "\"");
var evt2kill: Number = 0;
for (var str: String in listeners[type]) {
if (listeners[type][str]) {
evt2kill++;
}
}
if (evt2kill == 0) {
delete listeners[type];
//trace("no more listeners for event : " + "\"" + type + "\"");
return true;
}
return true;
}
}
}
}
}
delivered as is ;)
We take for granted things like jQuery that do this easily, but obviously we don't have that luxury in AS3.
well there is this: http://tech.nitoyon.com/blog/2008/01/as3query_alpha.html
I asked myself this question also a lot of times. Still haven't figured out an ultimate solution to the problem. Iterating through parents and parents is definately a way but has to be taken with caution, cause relations might change in your application during runtime. I wrote a simple method a few days ago that lets you iterate through all parents of a given object. Definitely not an elegant solution but it works so far. the SWIZ framework also offers good methods to facilitate the communication between objects via code injection and Event mediation. Maybe worth a look...

Returning from Flex/ActionScript 3 Responder objects

I need to return the value from my Responder object. Right now, I have:
private function pro():int {
gateway.connect('http://10.0.2.2:5000/gateway');
var id:int = 0;
function ret_pr(result:*):int {
return result
}
var responder:Responder = new Responder(ret_pr);
gateway.call('sx.xj', responder);
return id
}
Basically, I need to know how to get the return value of ret_pr into id or anything that I return from that function. The responder just seems to eat it. I can't use a public variable somewhere else because this will be running multiple times at once, so I need local scope.
This is how I'd write a connection to the AMF server, call it and store the resulting value. Remember that the result won't be available instantly so you'll set up the responder to "respond" to the data once it returns from the server.
public function init():void
{
connection = new NetConnection();
connection.connect('http://10.0.2.2:5000/gateway');
setSessionID( 1 );
}
public function setSessionID(user_id:String):void
{
var amfResponder:Responder = new Responder(setSessionIDResult, onFault);
connection.call("ServerService.setSessionID", amfResponder , user_id);
}
private function setSessionIDResult(result:Object):void {
id = result.id;
// here you'd do something to notify that the data has been downloaded. I'll usually
// use a custom Event class that just notifies that the data is ready,but I'd store
// it here in the class with the AMF call to keep all my data in one place.
}
private function onFault(fault:Object):void {
trace("AMFPHP error: "+fault);
}
I hope that can point you in the right direction.
private function pro():int {
gateway.connect('http://10.0.2.2:5000/gateway');
var id:int = 0;
function ret_pr(result:*):int {
return result
}
var responder:Responder = new Responder(ret_pr);
gateway.call('sx.xj', responder);
return id
}
This code is never going to get you what you want. You need to use a proper result function. The anonymous function responder return value will not be used by the surrounding function. It will always return 0 in this case. You are dealing with an asynchronous call here, and your logic needs to handle that accordingly.
private function pro():void {
gateway.connect('http://10.0.2.2:5000/gateway');
var responder:Responder = new Responder(handleResponse);
gateway.call('sx.xj', responder);
}
private function handleResponse(result:*):void
{
var event:MyCustomNotificationEvent = new MyCustomNotificationEvent(
MyCustomNotificationEvent.RESULTS_RECEIVED, result);
dispatchEvent(event);
//a listener responds to this and does work on your result
//or maybe here you add the result to an array, or some other
//mechanism
}
The point there being using anon functions/closures isn't going to give you some sort of pseudo-syncronous behavior.

Resources