I have an array collection object 'invArrayCol' which holds some data. I also have a datagrid. I have set dataProvider as invArrayCol.I displays the data properly when i use it with data grid. But the same invArrayCol shows null when used anywhere other than datagrid. I wrote this code
protected function titlewindow1_creationCompleteHandler(event:FlexEvent):void
{
Cgt=new CgtSRObject();
var autoobj:CSAutoNumberType=new CSAutoNumberType();
autoobj.addEventListener(ResultEvent.RESULT,getInvNubmer);
autoobj.getInvNo(invoiceType);
trace(robj.salesPerson_Id);
getSalesReturnCgt.token=csInvoicePrint.getCgtData(robj.receive_Id);
getSalesReturnCgt.addEventListener(ResultEvent.RESULT,getInvArrList);
trace(Cgt.sr_no);
datagrid_dataprovider=new ArrayCollection();
datagrid_dataprovider=invArrayCol;
calculateTotal();
}
This 2 lines set data to invArrayCol
getSalesReturnCgt.token=csInvoicePrint.getCgtData(robj.receive_Id);
getSalesReturnCgt.addEventListener(ResultEvent.RESULT,getInvArrList);
But here it gives value of invArrayCol as null.
datagrid_dataprovider=new ArrayCollection();
datagrid_dataprovider=invArrayCol;
Please tell me some way out of this.
The ResultEvent's result may return an ObjectProxy, in case the data is of length 1. Casting via 'as' would lead to a silent failing of the cast. So simply checking the type of the result would let you determine if the result can be used directly or if you have to wrap an ArrayCollection around it.
// This happens asynchronously, should have no effect in the function
getSalesReturnCgt.addEventListener(ResultEvent.RESULT,getInvArrList);
Also, the
// datagrid_dataprovider=new ArrayCollection(); // This line is obsoloete
datagrid_dataprovider=invArrayCol; // invArrayCol will get its value later
So, it looks like your expectation is for some code to have it executed synchronously, but it is always working asynchronously.
Related
I have a function in Adobe Flex 4 (ActionScript 3) that accepts an object and returns an ArrayCollection...
If a certain global variable is set to true, I want the function to delay itself for 3 seconds before running. Otherwise I want the function to run as normal.
The problem is, if I use a Timer, that timer calls a separate function, and that function cannot return anything to my calling function, nor can the function it calls accept any parameters, so it's not like I can call my own function recursively after the TimerComplete event fires... And a recursive call wouldn't work anyway, because it would return the ArrayCollection to the timer-result function, not to the original calling function...
I need a delay within the function, not a delay that causes me to go outside that function. But I cannot figure out how to do it.
Something like this is what I need to do:
private function createArrayCollection(myObject:Object):ArrayCollection {
var myArrayCollection:ArrayCollection = new ArrayCollection();
if (globalWaitBoolean) {
//delay here for 3 seconds, somehow
}
//Here I do the stuff that uses the info in myObject to figure out what to
//put into the ArrayCollection I want to return
return (myArrayCollection);
}
So... Any ideas on how to accomplish this without calling an external Timer function that cannot return an object back to my original function?
Thanks,
The way you want it you will have your whole application to lag for 3 seconds, unresponsive to any user input and external events. But it is possible, sure:
import flash.utils.getTimer;
private function createArrayCollection(myObject:Object):ArrayCollection
{
var myArrayCollection:ArrayCollection = new ArrayCollection;
if (globalWaitBoolean)
{
var waitUntil:int = getTimer() + 3000;
// Method getTimer() returns time in ms passed since app start.
// So you just have to wait until it is greater than appointed time.
while (getTimer() < waitUntil)
{
// Do nothing.
}
}
return (myArrayCollection);
}
Still, if you want to do it in a correct way of doing it:
import flash.utils.setTimeout;
private function callerMethod():void
{
// Blah blah blah.
// ...
// Finally.
createArrayCollection(sourceData, asyncResult);
}
private function createArrayCollection(myObject:Object, handler:Function):void
{
var result:ArrayCollection = new ArrayCollection;
if (globalWaitBoolean) setTimeout(handler, 3000, result);
else handler(result);
}
private function asyncResult(source:ArrayCollection):void
{
// The rest of your processing code.
}
Normal (synchronous) code flow would not return until the value is prepared, so should you desire to actually wait for 3 seconds while not allowing your app to do anything, use getTimer() approach from #Organis's answer. If you'll go for an asynchronus result, you'll need to face and overcome some more problems.
First, when do you expect your returned ArrayCollection to actually arrive. Speaking of code design, asynchronous code requires a whole lot of assumptions, thread safety etc etc, and even while AS3/Flash does not have true multithreading unless you count Workers, the code flow with events is not as obvious. So, whoever called your createArrayCollection() MUST NOT expect value returned from it right away. So, speaking about your direct question, NO, you can't avoid timers of some sort if you desire a responsive application. But you can use them with an approach that would involve an indirectly returned result.
Second, whether there might be concurring requests for more array collections from objects if your app would require these - you have to prepare for any kind of interference that might be caused by this. Say your function is triggered by a button click - what if that button would get clicked more than once in 3 seconds?
Third, actual route to processing code is not direct with asynchronous return. You need either a callback, an event handler (which is essentially a semi-native callback), a code that periodically checks for value presence (enter frame handler, etc) or a similar trick to gather the value that's returned asynchronously, and then transfer it to any relevant code that would process it further. Therefore, you would need to design an interface capable of receiving complex data (source object forward, array collection backward) and then carefully test it against all the possible cases and flaws.
An example of implementing all that is very long, I'll try to outline it somehow. Ler's assume you have a sort of "server" class that accepts requests for data and processes it synchronously (no wait) or asynchronously (wait). It accepts a source object of type "T" and provides a newly created object of type ArrayCollection, supplied as a parameter to whatever callback function sent to it. Also it accepts a delay (a simple way to show sync/async return would be a boolean, but why not getting an int?) as a parameter, and guarantees (to the extent of event model limitations) that after this delay the callback will be called ASAP. The architecture will then look like this:
class Processor {
Dictionary requests; // here all the requests that are delayed will be stored
public function dpr(source:T,callback:Function,delay:int=0):void{...}
// creates requests and stores them
private function syncProcess(source:T):ArrayCollection {...}
// whatever routine you want to get variably delayed
private function processTimeout(e:Event=null):void {...}
// processes events from "setTimeout()" and calls callbacks
}
Note that asynchronous approach forced to create three more entities than a synchronous one. First is the request holding structure (the dictionary here), second is timeout event handler, third is whatever callback you'll desire to get called when the data is ready. The code flow would go like this:
Synchronous call would result in the callback directly called from within the class: request->processTimeout->syncProcess()->callback. Asynchronous call will have the callback called from within Timer::timerComplete event handler via setTimeout called within request, with data that originally came from request stored in requests.
You could use an embedded/inline function:
private function createArrayCollection(myObject:Object):ArrayCollection {
var myArrayCollection:ArrayCollection = new ArrayCollection();
if (globalWaitBoolean) {
var milliseconds:int = 3000;
//delay here for 3 seconds
setTimeout(function()
{
//Here I do the stuff that uses the info in myObject to figure out what to
//put into the ArrayCollection I want to return
return (myArrayCollection);
},
milliseconds);
}
else
{
//Here I do the stuff that uses the info in myObject to figure out what to
//put into the ArrayCollection I want to return
return (myArrayCollection);
}
}
The inner function will have access to all local vars of the outer function.
I want to be able to access a instance on the stage dynamically by looping through an array containing Strings that describes the path.
private var clockKeeper:Array = new Array("LB.anim.clock.lbclock");
trace(stage.LB.anim.clock.lbclock.text);
for (var key in clockKeeper) {
trace(stage[clockKeeper[key]].text);
}
When i access it manually with the first trace statement, it works.
When i do it dynamically it seems like Flash tries to find an object named "LB.anim.clock.lbclock" not LB.anim....
How can i change this behaviour and make it work?
You should try splitting the "path" which should then consist of locally available names, and address each object in order. "Locally available names" means there should be stage.LB, and that object should have a property anim, etc etc.
function getObjectByPath(theRoot:DisplayObjectContainer,
thePath:String,separator:String='.'):DisplayObject
{
var current:DisplayObjectContainer=theRoot;
var splitPath:Array=thePath.split(separator);
while (splitPath.length>0) {
var named:DisplayObject = current.getChildByName(splitPath[0]);
var addressed:DisplayObject=current[splitPath[0]];
// either named or addressed should resolve! Otherwise panic
if (!addressed) addressed=named; else named=addressed;
if (!named) return null; // not found at some position
splitPath.shift();
if (splitPath.length==0) return named; // found, and last
current=named as DisplayObjectContainer;
if (!current) return null; // not a container in the middle of the list
}
// should never reach here, but if anything, just let em
return current;
}
This provides two ways of resolving the path, by name or by property name, and property name takes precedence. You should then typecast the result to proper type.
Yes, call this as follows:
trace((getObjectByPath(stage,clockKeeper[key]) as TextField).text);
A question about ItemRenderers: let's say I have an ArrayCollection that is my application data sitting inside a global object. I them populate a sparks list with this data, setting the ArrayCollection as the dataProvider.
So each ItemRenderer gets a copy of an item sitting in the array. You can override the "set data" method to set the data something more domain-specific. The problem is that the data is a copy of the original item.
Now let's say we want to add some data to the item while inside the ItemRender. For example, it could call a method on the item telling it to load some details about itself, or maybe we allow the user to modify something on the item.
Obviously, we can't do any of this if we are operating on a copy because it will be thrown away as soon as the ItemRenderer is destroyed and the original object doesn't know anything about what happened.
So what's the best practice? Should I just use the itemIndex of the renderer to pull out the original item from my global array like this:
{globalArrayCollection}.getItemAt(this.itemIndex)
But it seems kind of clunky to me. Is there a best practice for dealing with this?
Not sure I'm following but it sounds like you're looking for a way to get at your item renderer to set/change a value.
You could go about accessing a method on the renderer directly.
In your renderer:
public function setSomeValue(aValue:String):void{
someString = aValue;
}
You would also set the data on your ArrayCollection as well.
To access this method you would use this:
var dataGroup:DataGroup = list.dataGroup;
var itemRenderer:YourItemRenderer = dataGroup.getElementAt(list.selectedIndex) as YourItemRenderer;
itemRenderer.setSomeValue("string");
Hmm, why do you think that original ArrayCollection won't change if you change values in itemRenderer? For me this works and initial ArrayCollection changes.
[Bindable]
protected var model:Model;
override public function set data(value:Object):void
{
super.data = value;
this.model = value as Model;
}
protected function changeValue():void
{
model.value = "newValue";
}
Or am I misunderstood something?
Im trying to understan a function that I found on the web.
Iknow what the function does, It get the information about the webcam in your computer and post it on the textArea,
But the individual line are just a bit confused.
Any help ?
Thanks
private var camera:Camera;
private function list_change(evt:ListEvent):void {
var tList:List = evt.currentTarget as List;
var cameraName:String = tList.selectedIndex.toString();
camera = Camera.getCamera(cameraName);
textArea.text = ObjectUtil.toString(camera);
}
private var camera:Camera;
This line creates a variable of the class type Camera. It does not create an instance of the variable.
private function list_change(evt:ListEvent):void {
This line is a standard function heading. Because the argument is a ListEvent, it makes me think that this function is probably written as an event handler. Because of the name of the function, it is most like listening to the change event on a list.
var tList:List = evt.currentTarget as List;
This line creates a reference to the list that dispatched the event, which caused this handler to be executed.
var cameraName:String = tList.selectedIndex.toString();
This line converts the selectedIndex to a string. It's a bit odd to convert an index to a string, as opposed to some value. But the reason they do that looks to be on the next line..
camera = Camera.getCamera(cameraName);
This uses that camera variable (defined back in line 1) and actually gets an instance of the camera. It uses the "cameraName" which makes me think that the list that dispatched this change event contains a list of cameras available on the system.
textArea.text = ObjectUtil.toString(camera);
This converts the camera object to a string and displays it in a text area. Normally you wouldn't try to do this as it provides no valuable data. A default object will display strings as [Object object] or something similar. Perhaps the camera object has a custom string function; I don't have experience with that. Normally, you'd want to access properties of the object to get useful information, not try this on the object itself.
}
This line is the end of the function. The open bracket was in the 2nd line of code in the function definition.
Firstly can some one please explain what is meant by Object and Function in a profiling environment.
Secondly, why does the Object and Function count increase when I repeatedly set the text property of a textfield:
override public function setLanguage(id:String):void
{
if (id == "en")
{
ui.title.text = _data.text.title.en;
ui.title.direction = Direction.LTR;
}
else if (id == "ae")
{
ui.title.text = _data.text.title.en;
ui.title.direction = Direction.RTL;
}
}
From Laurent:
Internally, TextField::text is most likely a getter/setter (since it needs to set a flag to update the text field display, also possibly update the HTML content, etc.) so when you set it you are effectively calling a function.
This means that TextField.text is implemented as a property getter and setter, so if you had to code it, you would see something like
private var _text:String="";
public function get text():String {
return _text;
}
public function set text(value:String):void {
_text=value;
}
Your Object count increases every time you reference (looking for a better word, don't kill me about this :P) an object (I trust you know what objects are), and your Function count increases every time you invoke a function.
So when you do something like
myTextField.text="Hello World";
you are referencing object myTextField and invoking its function set text(String);, causing your counts to increase by 1 each.
Internally, TextField::text is most likely a getter/setter (since it needs to set a flag to update the text field display, also possibly update the HTML content, etc.) so when you set it you are effectively calling a function.
What is it you don't understand about the difference between a Function and an Object? Could you be more specific?