Check to see if CallResponder is processing - apache-flex

I'm using Flash Builder 4.6. As a simple example, say I have the following application:
<?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:sdk="services.sdk.*">
<fx:Script>
<![CDATA[
private function btnGetValue_clickHandler():void
{
getValueResult.token = sdk.getValue();
}
private function getValueResultHandler():void
{
// ...
}
]]>
</fx:Script>
<fx:Declarations>
<sdk:SDK id="sdk" fault="{Alert.show(event.fault.faultString +'\n\n'+ event.fault.faultDetail, 'SDK ERROR');}" showBusyCursor="false"/>
<s:CallResponder id="getValueResult" result="getValueResultHandler()"/>
</fx:Declarations>
<s:Button id="btnGetValue" click="btnGetValue_clickHandler()" label="Get Value" />
</s:Application>
So when you click on the button, it calls a PHP file and when it gets a result, it calls getValueResultHandler(). Easy enough.
But what if the response from the PHP file takes a second or two and the user clicks the button rapidly? Then the result handler function may not get called every time, since the call responder gets a new token before it received the last response.
Is there a standard way of resolving this issue? I came up with the following workaround, and it works fine, but it seems like this issue would be common enough to have a more built-in solution.
My workaround is:
var getValueResultProcessing:Boolean = false;
private function btnGetValue_clickHandler():void
{
var interval:uint = setInterval(function():void
{
if (!getValueResultProcessing)
{
getValueResultProcessing = true;
getValueResult.token = sdk.getValue();
clearInterval(interval);
}
}, 100);
getValueResult.token = sdk.getValue();
}
private function getValueResultHandler():void
{
getValueResultProcessing = false;
// ...
}
Any better way of resolving this issue?
EDIT:
One of my actual scenarios for which I need a solution is when looping:
private function btnGetValue_clickHandler():void
{
var arr:Array = new Array("value1", "value2", "value3");
for each (var value:String in arr)
getValueResult.token = sdk.getValue(value);
}
So to solve this problem I make arr a global variable and use the setInterval method I described before to shift off the first element and then do the same exact thing in the result function until arr is empty.
Again, looking for a more "standard" solution, if such a thing exists.

Instead of using CallResponder, you might want to manage the responses to service calls yourself. HTTPService (or similar Flex services) return an AsyncToken when you call the HTTPService.send() method. You can then add 'result' and 'fault' handlers by using the AsyncToken.addResponder() method.
If you do it this way, each responder will trigger it's result or fault handler as appropriate. Then use the last approach as suggest by Sam DeHaan, where you store each token in an array when the call is made, and remove the token from the array when the 'result' or 'fault' handler is executed. When removing a token from the array, you can re-enable the button if the array is empty.
You do it something like this (typing code from memory):
var service:HTTPService = new HTTPService();
// configure the service...
var token:AsyncToken = service.send();
// resultHandlerMethod and faultHandler method are functions that you should define
// their signatures look like this:
// resultHandlerMethod(event:ResultEvent)
// faultHandlerMethod(event:FaultEvent)
token.addResponder(new Responder(resultHandlerMethod, faultHandlerMethod);

If the call is button based, disable the button on click. Enable the button at the end of the callback. Simple.

Related

Flex: getting the previous value of a combobox when it changes

I need to know the value from which a combo box is changing, when it changes. I've been all through the documentation for the events, and none of them let you know what the value is before user interaction changes it. (currentStateChanging is a total red herring!) Handling the open event and saving the value isn't a solution, because there are other ways to change the value.
I'm using the Flex 3.5 SDK.
Something like this?
var currentVal : Object;
private function onChange(newVal) : void {
// currentVal stores your previous value - do something with it
currentVal = newVal;
}
<mx:ComboBox change="onChange(event.target.selectedItem)"/>
I just used the "changing" event on a Spark ComboBox to solve this very problem but it's not available on the mx version
Also - see this
I've come to the conclusion that there isn't an answer :( The best workaround is to override all possible ways there are to set the value of a combo box, plus handle any events that involve the user changing the value, back up that value and then you have a trail of previous values. Then, put a lot of comments saying
this is a 3.5-necessary kluge! If doing this on another SDK you might have to change it!
=======
I've come up w/a solution, but it's not perfectly reliable (since it makes assumptions about how it will work in other SDKs) and its elegance is wanting:
<mx:ComboBox xmlns:mx="http://www.adobe.com/2006/mxml" valueCommit="OnChangeAnyway()" change="OnChange()">
<mx:Metadata>
[Event(name='traceable_change', type="assets.LineComboChangeEvent")]
</mx:Metadata>
<mx:Script><![CDATA[
public static const CHANGE:String = 'traceable_change';
private var m_oOld:Object;
private var m_oNew:Object;
private var m_bCallLaterPending:Boolean = false; //This is necessary, because I found OnChangeAnyway() could be called any number of times before OnChange() is
private function OnChange():void {
var oNewEvent:LineComboChangeEvent = new LineComboChangeEvent(CHANGE, m_oOld); //there's nothing special in this class
dispatchEvent(oNewEvent);
}
private function OnChangeAnyway():void {
if (!m_bCallLaterPending) {
m_bCallLaterPending = true;
callLater(function ():void { m_bCallLaterPending = false;}, []); //presumably, whatever is passed to callLater() will be executed after everything else currently queued
m_oOld = m_oNew;
m_oNew = value;
}
}
]]></mx:Script>
m_oNew is obviously redundant because that value will be available to whatever handles traceable_change, but it does explain why I have to barrel-shift these objects.
There are a couple of reasons why I don't consider this reliable:
It assumes that the valueCommit handler will be called ahead of the change one. On my system, it always seems to, but I don't see that promise anywhere.
It assumes that whatever callLater() calls will be called after change is. See concerns for 1.

Is there init() method in flex like Servlet init() only one time execute

Flex having any init(), destroy() method. Like Servlet init()method will run at Application initialize and never call it again if Refresh the Page also.
I would suggest not using initialize event, and instead use creationComplete. All UIComponent dispatch that event when they are finished constructing themselves AND their children. This event is executed once after the component has been initialized, had a chance to measure itself, perform layout, and added to the stage.
<mx:Application ... creationComplete="init()"/>
<mx:Script>
private function init() : void {
... // put your initialization routine here
}
</mx:Script>
</mx:Application>
All flex components, including the root "application" component have an "initlize" event that you can listen to and handle.
If you'd like it to only run ONCE, regardless of refresh, you'd need to store a variable somehow, such as with a local shared object. That's pretty easy to do:
private function onInit():void{
var appSO:SharedObject = SharedObject.getLocal("yourappdata");
if(appSO.size < 0){
//do your init code
appSO.data.initialized = true;
appSO.flush();
}
}

Flex/BlazeDS - resultHandler per function call not per RemoteObject?

I've followed this tutorial to get some Flex code to call Java code hosted on a Tomcat server.
This is how my RemoteObject and the Button to call the remote function is declared:
<mx:RemoteObject id="productService" destination="productJavaService" result="resultHandler(event)" fault="faultHandler(event)"/>
<mx:Button label="Get all Products" click="productService.getAllProducts()" />
These are the definitions of the resultHandler and faultHandler functions:
private function resultHandler(event:ResultEvent):void
{
products = event.result as ArrayCollection;
}
private function faultHandler(event:FaultEvent):void
{
Alert.show(event.fault.faultString);
}
The obvious problem with this for me is that the resultHandler is associated with the RemoteObject as a whole rather than the individual function. If I add a new function such as "getSingleProduct" then obviously a different resultHandler will need to be used. How do I specify the resultHandler at function level?
You can define a method property under a RemoteObject, in your case, it would be getAllProducts(); You can do so like this:
<mx:RemoteObject id="Server" destination="ServerDestination" fault="faultHandler(event)">
<mx:method name="getAllProducts" result="getAllProductsHandler(event)"/>
<mx:method name="getOneProduct" result="getOneProductHandler(event)"/>
</mx:RemoteObject>
Just wanted to add: in case anyone wants to achieve this with actionscript, you can do this with actionscript by adding a Responder to the AsyncToken returned from the service call:
var responder:Responder = new Responder(onGetOneProductResult, onGetOneProductFault);
var token:AsyncToken = Server.getOneProduct();
token.addResponder(responder);
private function onGetOneProductResult(event:ResultEvent):void {
// event.result is the data you sent back from the server
var result:Object = event.result;
}
private function onGetOneProductFault(event:FaultEvent):void {
trace("onGetOneProductFault : "+event.fault.faultString);
}

Is it possiable send request to Google and Yahoo API at the same time?

In my project I used Google adwords API and also Yahoo API. I want to send request to two API at same time by using flex.
Is it possible to send request to same time. If not how can I do ?
Since flex is event based, this should be pretty simple. Just create 2 URLRequest objects (I am assuming both APIs are REST-based), and set your GET variables. Create a loadComplete event listener for each of the URLRequest object, and in each of the eventlisteners, write code that processes the results from both APIs.
That should do it.
Not "at the same time," no. For one thing, you've got only one (background) thread to work with, and that thread can only do one thing at a time, so however you write your code, one of the requests will always be initiated first.
If what you're asking is whether you can send out a request to both services in a single call, then the answer is again no (since both will have different URLs, and the ActionScript APIs don't take arrays as URL parameters, they only take strings).
That said, I suppose you could "fake it," so to speak, by having your Flex app call a service you wrote yourself -- e.g., in C# -- and have that service call (synchronously) Google, then Yahoo, then return control back to your Flex app along with a modified result set consisting of whatever happened to come back from Google and Yahoo, respectively.
Unless I'm misunderstanding (or overthinking) the question. ;)
Here's some sample code illustrating one way to handle calling two services -- not concurrently, but one right after the other, using mx.rpc.HTTPService:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="onCreationComplete()">
<mx:Script>
<![CDATA[
import mx.rpc.http.HTTPService;
import mx.rpc.events.ResultEvent;
import mx.rpc.events.FaultEvent;
private function onCreationComplete():void
{
callGoogle();
callYahoo();
}
private function callGoogle():void
{
var svc:HTTPService = new HTTPService();
svc.url = "http://api.google.com/someservice.xml";
svc.addEventListener(ResultEvent.RESULT, onGoogleResult, false, 0, true);
svc.addEventListener(FaultEvent.FAULT, onGoogleFault, false, 0, true);
svc.send();
}
private function callYahoo():void
{
var svc:HTTPService = new HTTPService();
svc.url = "http://api.yahoo.com/someservice.xml";
svc.addEventListener(ResultEvent.RESULT, onYahooResult, false, 0, true);
svc.addEventListener(FaultEvent.FAULT, onYahooFault, false, 0, true);
svc.send();
}
private function onGoogleResult(event:ResultEvent):void
{
trace(event.result);
}
private function onGoogleFault(event:FaultEvent):void
{
trace(event.fault.message);
}
private function onYahooResult(event:ResultEvent):void
{
trace(event.result);
}
private function onYahooFault(event:FaultEvent):void
{
trace(event.fault.message);
}
]]>
</mx:Script>
</mx:Application>
Hope that helps! Post back with comments and I'll keep an eye out.

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