Flex- Singleton's properties Binded to HttpService properties are not being updated - apache-flex

I have the following Singleton:
package singletons{
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IOErrorEvent;
import flash.net.SharedObject;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.net.registerClassAlias;
public class AppConfiguration extends EventDispatcher
{
public static const SUCCESSFULCONFIGURATION:String = "ConfigurationSuccessful";
public static const CONFIGURATIONERROR:String = "ConfigurationError";
private var _userID:Number;
private var _serverIP:String;
private var _serverPort:Number;
private var _serverURL:String;
private static var _instance:AppConfiguration;
public static function getInstance():AppConfiguration
{
if (_instance == null)
{
_instance = new AppConfiguration();
registerClassAlias( "singletons.AppConfiguration", AppConfiguration );
}
return _instance;
}
public function initialize():Boolean
{
var configFile:SharedObject = SharedObject.getLocal("InternalConfiguration");
if (configFile.data.internalConfiguration == null)
{
return false;
}
var localConf:AppConfiguration = AppConfiguration(configFile.data.internalConfiguration);
_userID = localConf.UserID;
_serverIP = localConf.ServerIP;
_serverPort = localConf.ServerPort;
_serverURL = "http://" + _serverIP + ":" + _serverPort;
return true;
}
public function setInternalConfiguration(userID:Number, serverIP:String, serverPort:Number):void
{
_userID = userID;
_serverIP = serverIP;
_serverPort = serverPort;
_serverURL = "http://" + _serverIP + ":" + _serverPort;
var configurationRequest:URLRequest = new URLRequest(_serverURL + "/getHello?userID=" + _userID);
var requestLoader:URLLoader = new URLLoader();
requestLoader.addEventListener(Event.COMPLETE, getConfigurationHandler);
requestLoader.addEventListener(IOErrorEvent.IO_ERROR, getConfigurationErrorHandler);
requestLoader.load(configurationRequest);
}
private function getConfigurationHandler(event:Event):void
{
// 1.2.- Asignar los valores de las variables obtenidas a las variables locales
var configFile:SharedObject = SharedObject.getLocal("InternalConfiguration");
configFile.data.internalConfiguration = this as AppConfiguration;
configFile.flush();
var successEvent:Event = new Event(SUCCESSFULCONFIGURATION);
dispatchEvent(successEvent);
}
private function getConfigurationErrorHandler(event:IOErrorEvent):void
{
var failedEvent:Event = new Event(CONFIGURATIONERROR);
dispatchEvent(failedEvent);
}
public function get UserID():Number
{
return _userID;
}
public function get ServerIP():String
{
return _serverIP;
}
public function get ServerPort():Number
{
return _serverPort;
}
public function get ServerURL():String
{
return _serverURL;
}
}
}
And when I try to bind two singleton's properties ServerURL and UserID into the properties URL and Request of my HTTPService, the values don't get updated in the HTTPService after they have being modified. The code that makes the binding is the following:
<fx:Declarations>
<s:HTTPService id="getStatusService"
url="{internalConfiguration.ServerURL}/getObject"
result="getStatusHandler(event)" fault="getStatusErrorHandler(event)">
<s:request>
<codec>{internalConfiguration.CodecID}</codec>
</s:request>
</s:HTTPService>
</fx:Declarations>
And I when I do the getStatusService.send(), it goes to "null/getObject".
Anyone knows why the variables are not being updated inside the HTTPService?
Thanks for the help.
Sebastián

internalConfiguration.ServerURL is not being updated as initialize() method is never called (at least in code you've provided.)
ServerURL field is empty by default so that is why it's null in httpservice. Take into account that ServerURL should be bindable. Also ServerURL should has setter, otherwise binding will not be triggered.
And don't forget that config has to be bindable either:
[Bindable]
private var internalConfiguration:AppConfiguration = AppConfiguration.getInstance();

Related

how to call an external function from inside a class?

i want to call an external function inside a class. thats the code;
in checkConnectionStatus function,
this[_funcNameForSucceededCon].apply(); doesnt work because "this" is the class, not the Application. How can i reach Application at this time or what can i do?
any help will be greatly appreciated.
best regards,
mira.
package myLibrary
{
import air.net.URLMonitor;
import flash.events.Event;
import flash.events.StatusEvent;
import flash.net.URLRequest;
public class connectionControl
{
private var _urlReq:URLRequest;
private var _urlMonitor:URLMonitor;
private var _funcNameForSucceededCon:String;
private var _funcNameForFailedCon:String;
public function connectionControl(targetURL:String, funcNameForSucceededCon:String, funcNameForFailedCon:String)
{
_urlReq = new URLRequest(targetURL);
_urlMonitor = new URLMoniotor(_urlReq);
_urlMonitor.addEventListener(StatusEvent.STATUS, checkConnectionStatus);
_funcNameForSucceededCon = funcNameForSucceededCon;
_funcNameForFailedCon = funcNameForFailedCon;
if(_urlMonitor.running == false)
{
_urlMonitor.start();
}
else
{
_urlMonitor.stop();
_urlMonitor.start();
}
}
private function checkConnectionStatus(e:Event):void
{
_urlMonitor.removeEventListener(StatusEvent.STATUS, checkConnectionStatus);
if(_urlMonitor.available)
{
this[_funcNameForSucceededCon].apply();
}
else
{
this[_funcNameForFailedCon].apply();
}
}
}
}
You have passed the name of the function to be serving as a callback. Use instead the function itself and pass it to connectionControl.
public class connectionControl
{
private var _funcSucceededCon:Function;
private var _funcFailedCon:Function;
public function connectionControl(targetURL:String, funcSucceededCon:Function, funcFailedCon:Function)
{
_urlReq = new URLRequest(targetURL);
_urlMonitor = new URLMoniotor(_urlReq);
_urlMonitor.addEventListener(StatusEvent.STATUS, checkConnectionStatus);
_funcSucceededCon= funcSucceededCon;
_funcFailedCon= funcFailedCon;
...
And:
if(_urlMonitor.available)
{
_funcSucceededCon();
}

AS3 / Flex 3 Staggered Remoting + Queue

I am trying to make a class that staggers the net connections calls by a certain amount to not put too much pressure on my server, and so I don't have a dozen net connectors running around in my code.
I want a class that I can send a call command to, the class adds the call to the queue, and then about every one second it see if anything is in the queue, and if so calls it. This is what I have so far.
package net
{
import flash.events.TimerEvent;
import flash.net.NetConnection;
import flash.net.Responder;
import flash.utils.Timer;
public class Server
{
private static var gateway:String = "http://localhost/gateway.php";
private static var queue:Vector.<ServerNode>
private static var nc:NetConnection;
private static var instance:Server = null;
private static var res:Responder;
private var timer:Timer;
public function Server(e:ServerEnforcer) {
nc = new NetConnection();
queue = new Vector.<ServerNode>();
timer = new Timer(1000);
timer.addEventListener(TimerEvent.TIMER, execCall);
timer.start();
}
public static function getInstance():Server {
if (instance == null) {
instance = new Server(new ServerEnforcer);
}
return instance;
}
private function execCall(e:Event):void {
if (queue.length > 0) {
var node:ServerNode = queue.pop();
nc.call(node.method, node.res, node.args);
}
}
public function call(method:String, success:Function, failure:Function, ...args):void {
queue.unshift(new ServerNode(method, success, failure, args));
}
private function serverFailure(event:Object):void {
trace("Server Failure : " + event.description);
}
}
}
import flash.net.Responder;
class ServerEnforcer { }
class ServerNode {
public var method:String;
public var success:Function;
public var failure:Function;
public var args:Array;
public var res:Responder
public function ServerNode(_method:String, _success:Function, _failure:Function, _args:Array) {
method = _method;
success = _success;
failure = _failure;
res = new Responder(success, failure);
args = _args;
}
}
Now when I call
Server.getInstance().call("Fetch.getData", parseAllData, onError)
public function parseAllData(event:Object):void {
trace("Victory!");
}
public function onError(event:Object):void {
trace("Error :" + event);
}
absolutely nothing happens. Any idea why or a point in the right direction why this isn't working?
You created an instance of the NetConnection, but haven't actually initiated a connection with the server.
In other words,
nc.connect(gateway);
is missing.
See NetConnection documentation for more information on that class.

Simplify AS3 binding/event-dispatching code

There are 3 properties (example 1):
[Bindable] public var name:String;
[Bindable] public var email:Number;
[Bindable] public var address:Boolean;
I needed to have 3 helper methods that will be bindable too (example 2):
[Bindable] public var name:String;
[Bindable] public var email:Number;
[Bindable] public var address:Boolean;
public function get nameIsOk():Boolean { return !Strings.isEmpty(name) }
public function get emailIsOk():Boolean { return email == 3 }
public function get addressIsOk():Boolean { return address }
Sure, the code above doesn't work. I made it work by doing this (example 3):
private var _name:String
[Bindable("nameChanged")]
public function get name():String { return _name }
public function set name(v:String):void { _name = v; dispatchEvent(new Event("nameChanged")) }
[Bindable("nameChanged")]
public function get nameIsOk():Boolean { return !Strings.isEmpty(name) }
private var _email:Number
[Bindable("emailChanged")]
public function get email():Number { return _email }
public function set email(v:Number):void { _email = v; dispatchEvent(new Event("emailChanged")) }
[Bindable("emailChanged")]
public function get emailIsOk():Boolean { return email == 3 }
private var _address:Boolean
[Bindable("addressChanged")]
public function get address():Boolean { return _address }
public function set address(v:Boolean):void { _address = v; dispatchEvent(new Event("addressChanged")) }
[Bindable("addressChanged")]
public function get addressIsOk():Boolean { return address }
It does work, but now it is bloated.
Is there a way to reduce this code (example 3) to something smaller (like example 2)?
UPDATE:
Kudos to just_a_dude for nice answer. Here is the final version:
[Bindable] public var name:String;
[Bindable] public var email:Number;
[Bindable] public var address:Boolean;
public function Remixer() {
for each (var f:String in Strings.split("name email address")) {
ChangeWatcher.watch(this, f, onChange)
}
}
private function onChange(e:PropertyChangeEvent):void {
dispatchEvent(new Event(e.property + "Changed"))
}
[Bindable("nameChanged")]
public function get nameIsOk():Boolean { return !Strings.isEmpty(name) }
[Bindable("emailChanged")]
public function get emailIsOk():Boolean { return email == 3 }
[Bindable("addressChanged")]
public function get addressIsOk():Boolean { return address }
Not sure if this is what you're looking for but you can use mx.binding.utils.ChangeWatcher to "watch" for property changes
<?xml version="1.0" encoding="utf-8"?>
<mx:Script>
<![CDATA[
import mx.events.PropertyChangeEvent;
import mx.binding.utils.ChangeWatcher;
[Bindable] public var firstName:String;
[Bindable] public var email:Number;
[Bindable] public var address:Boolean;
private var _watcher:ChangeWatcher;
private function init():void {
ChangeWatcher.watch(this, "firstName", propertyChangeHandler);
ChangeWatcher.watch(this, "email", propertyChangeHandler);
ChangeWatcher.watch(this, "address", propertyChangeHandler);
firstName = "foo";
email = 0;
address = true;
firstName = "bar";
email = 1;
address = false;
}
protected function propertyChangeHandler(event:PropertyChangeEvent):void {
var prop:Object = event.property;
var name:String = prop.toString() + "Changed";
// trace(name); // displays firstNameChanged or emailChanged or addressChanged
dispatchEvent(new Event(name));
}
]]>
</mx:Script>
Let me know if this helps
Cheers
I'm not sure if you need getters, but if not, a nice way to do it, is to just use a single function, and put your bindable strings as arguments.
if you put this in your object:
public function isOk(s:String):Boolean
{
return !Strings.isEmpty(s)
}
You would use it like this:
<mx:CheckBox selected="{yourObject.isOk(yourObject.name)}" />
Generally, if you put a function inside the "{}" with parameters which are bindable, it will be called each time that parameter changes.
I would encapsulate your functionality in a class. Do not repeat yourself :)

Arraycollection not capturing the thrown event?

I have a collection of objects and each object throws an event every time its value gets updated. Im trying to capture that event by adding a listener to the arraycollection that holds it (see main class) but its not working. Honestly I'm not sure this is the correct approach.
I'm avoiding using Collection.CHANGE because it fells into an infinite recursion ultimately ends in a stack overflow. Any ideas?
[Bindable]
public class NamesVO {
public var steveList:ArrayCollection; // array of SteveVO objects
public function NamesVO() {
steveList = new ArrayCollection();
}
public function rename():void {
for each(var steve:SteveVO in steveList) {
steve.rename();
}
}
}
[Bindable]
public class SteveVO extends EventDispatcher {
public static const VALUE_CHANGED:String = "VALUE_CHANGED";
public var code:String;
public var name:String;
public var _quantity:Number;
public function SteveVO() {
this.code = "";
this.name = "";
_quantity = 0;
}
public function get quantity():Number {
return _quantity;
}
public function set quantity(quantity:Number):void {
_quantity = quantity;
dispatchEvent(new Event(VALUE_CHANGED));
}
public function rename():void {
name = code + " - " + _quantity;
}
}
Main class:
names = new NamesVO();
names.steveList.addEventListener(SteveVO.VALUE_CHANGED, function():void {
names.rename(); // this anon function is not being executed!!
});
var steve:SteveVO = new SteveVO();
names.steveList.addItem(steve);
// names is bound on a datagrid and uses itemeditor for each SteveVO object
The VALUE_CHANGED event is not dispatched by the steveList array Collection so won't be detected by your listener. You could encapsulate the functionality you want inside the NamesVO class by detecting when an item is added to the array collection and adding a listener to the new steveVO object that dispatches the same event from NamesVO. Then just listen for that event in your main class.
Is there a reason to change all the names when one quantity is changed. Would it be better simply to call rename inside the set function of the steveVO class?
To implement the change:
import flash.events.Event;
import mx.collections.ArrayCollection;
import mx.events.CollectionEvent;
import mx.events.CollectionEventKind;
[Bindable]
public class namesVO
{
public var steveList:ArrayCollection; // array of SteveVO objects
public function namesVO()
{
steveList = new ArrayCollection();
steveList.addEventListener(CollectionEvent.COLLECTION_CHANGE,collChanged);
}
private function collChanged(e:CollectionEvent):void
{
if (e.kind == CollectionEventKind.ADD)
e.items[0].addEventListener(steveVO.VALUE_CHANGED,valueChanged);
}
private function valueChanged(e:Event):void
{
dispatchEvent(new Event(steveVO.VALUE_CHANGED));
}
public function rename():void
{
for each(var steve:steveVO in steveList)
{
steve.rename();
}
}
}
In the main class use:
names = new namesVO();
names.addEventListener(steveVO.VALUE_CHANGED, function():void
{
names.rename();
});
steve = new steveVO();
names.steveList.addItem(steve);
steve.quantity = 12;
Of course this is only an example and only includes the case where one item is added at a time.

Deep Binding in Flex

So I have a module in flex in which I add a custom component. I also have a class that handles the data I want to show, lets call this class DataHandler.
The DataHandler receives data from the back-end solution and then starts putting the data togheter for my Module and the custom component.
When the data is ready it dispatches an event that my Module catch. I send the new data in to my component.
example code for this in Module:
private function onDataChange(evt:Event=null):void
{
_customComponent.ItemData = _dataHandler.DataProvider;
}
The _customComponent then gets the data :
public function set ItemData(value:ItemDataVO):void
{
_itemdata = value;
}
// _itemdata is a custom class named ItemDataVO
Now in my custom component I just bind the data to my mxml components , for example
<mx:Label
text = "Text: {_itemdata.Text}"
fontFamily = "Hel"
fontSize = "12"
x = "83"
y = "40" />
When I get new data the label automaticly changes.
So far so good. But what I also have in my custom component is i List.
And this is my problem. When I bind the data to the List I do the following:
<mx:List
id = "_list"
dataProvider ="{_itemdata.Collection}"
itemRenderer = "components.renderers.CustomRenderer" />
// this _itemdata.Collection is an ArrayCollection that contains a collection of items based on a custom class.
The binding does not work, and I also get a varning for each item in the list at runtime:
warning: unable to bind to property 'parent' on class 'modules::CustomModule'
( I have also tried, as a workaround, to set the _list's itemrenderer each time the ItemData is set. The new listdata then update but I dont see any visual update in the list. )
Anyone knows how to make this binding work?
Regards Adlertz =)
Have you set the collection property on ItemDataVo to be bindable... something like
package
{
import mx.collections.ArrayCollection;
public class ItemDataVo
{
[Bindable]
public var text : String;
[Bindable]
public var collection : ArrayCollection
public function ItemDataVo()
{
}
}
}
I have made a simplified working example showing binding working on a model similar to yours (although there will obviously be differences) with binding working correctly so long as both the text and collection properties both have the Bindable meta data attached:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" xmlns:local="*" creationComplete="creationCompleteHandler(event)">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.FlexEvent;
[Bindable]
protected var itemData : ItemDataVo;
private function creationCompleteHandler(event : FlexEvent) : void
{
generateItemData();
}
protected function generateItemData() : void
{
itemData = new ItemDataVo();
itemData.text = "New Text With Random " + Math.random() * 100;
itemData.collection = generateCollection();
}
protected function generateCollection() : ArrayCollection
{
var arrayCollection : ArrayCollection = new ArrayCollection();
arrayCollection.addItem("New Item With Random " + Math.random() * 100);
arrayCollection.addItem("New Item With Random " + Math.random() * 100);
arrayCollection.addItem("New Item With Random " + Math.random() * 100);
arrayCollection.addItem("New Item With Random " + Math.random() * 100);
arrayCollection.addItem("New Item With Random " + Math.random() * 100);
arrayCollection.addItem("New Item With Random " + Math.random() * 100);
return arrayCollection;
}
private function fullClickHandler(event : MouseEvent) : void
{
generateItemData();
}
private function collectionClickHandler(event : MouseEvent) : void
{
itemData.collection = generateCollection();
}
]]>
</mx:Script>
<mx:VBox width="100%" height="100%">
<mx:VBox>
<mx:Label text="{itemData.text}" />
<mx:List dataProvider="{itemData.collection}" />
</mx:VBox>
<mx:HBox>
<mx:Button label="UPDATE FULL ITEM DATA" click="fullClickHandler(event)"/>
<mx:Button label="UPDATE COLLECTION" click="collectionClickHandler(event)"/>
</mx:HBox>
</mx:VBox>
</mx:Application>
In the past I added my List into my Module and it never gave me any binding problems. But yes it seems very strange that the component in between breaks the binding.
The object stored in the Collection looks like this:
package code.converters.objects
{
public class LineupLI
{
private var _plid:int;
private var _lastname:String;
private var _firstname:String;
private var _shirtnumber:int;
private var _onpitch:Boolean;
private var _distance:Number;
private var _topspeed:Number;
private var _goals:int = 0;
private var _subin:Boolean = false;
private var _subout:Boolean = false;
private var _redcard:Boolean = false ;
private var _yellowcard:Boolean = false;
private var _teamid:int;
private var _teamlongdesc:String;
private var _second_increase:Number;
[Bindable]
public function get OnPitch():Boolean { return _onpitch; }
public function set OnPitch(value:Boolean):void { _onpitch = value; }
[Bindable]
public function get ShirtNumber():int { return _shirtnumber; }
public function set ShirtNumber(value:int):void { _shirtnumber = value; }
[Bindable]
public function get PlId():int { return _plid; }
public function set PlId(value:int):void { _plid = value; }
[Bindable]
public function get FirstName():String { return _firstname; }
public function set FirstName(value:String):void { _firstname = value; }
[Bindable]
public function get LastName():String { return _lastname; }
public function set LastName(value:String):void { _lastname = value; }
[Bindable]
public function get Distance():Number { return _distance; }
public function set Distance(value:Number):void { _distance = value; }
[Bindable]
public function get TopSpeed():Number { return _topspeed; }
public function set TopSpeed(value:Number):void { _topspeed = value; }
[Bindable]
public function get Goals():int { return _goals; }
public function set Goals(value:int):void { _goals = value; }
[Bindable]
public function get SubIn():Boolean { return _subin; }
public function set SubIn(value:Boolean):void { _subin = value; }
[Bindable]
public function get SubOut():Boolean { return _subout; }
public function set SubOut(value:Boolean):void { _subout = value; }
[Bindable]
public function get RedCard():Boolean { return _redcard; }
public function set RedCard(value:Boolean):void { _redcard = value; }
[Bindable]
public function get YellowCard():Boolean { return _yellowcard; }
public function set YellowCard(value:Boolean):void { _yellowcard = value; }
[Bindable]
public function get TeamId():int { return _teamid; }
public function set TeamId(value:int):void { _teamid = value; }
[Bindable]
public function get TeamLongDesc():String { return _teamlongdesc; }
public function set TeamLongDesc(value:String):void { _teamlongdesc = value; }
[Bindable]
public function get SecondIncrease():Number { return _second_increase; }
public function set SecondIncrease(value:Number):void { _second_increase = value; }
}
}
Looks ok to me... i think there could be a couple of problems here. One is the binding not working and the second (as David Hanak stated) being why the warning
warning: unable to bind to property 'parent' on class 'modules::CustomModule'
is being thrown which doesn't seem to relate to any of your objects. I would try to solve the latter first and try and figure out why that is happening. You may find that this fixes the original binding problem.

Resources