I've inherited an application and am fixing a bug. There is a refresh button in a the application's mxml file and it has a click="refresh(null)". This works.
However we also want to do a refresh every five minutes automatically using a timer. There is code in an .as file (that is sourced' into the above mxml file) that uses a Timer and calls refresh(null) from within the .as file, but this doesn't seem to do anything.
Is this the right way to do this? Or do we need to explicitly reference the object we want to refresh? If so, how do we do that?
Code jisting (sorry, I'm on an isolated network and can't easily move code to here; if you can't read it then just ignore my question please):
foo.mxml:
<mx:Application ...>
<mx:Script source="funcs.as"/>
<mx:Image ... click="refresh(null)".../>
funcs.as:
// timer created
// timer started
// timer observer added, calls refreshScreen()
private function refreshScreen():void
{
refresh(null);
}
var timer:Timer = new Timer( 5000 );// 5 second intervals
timer.addEventListener(TimerEvent.TIMER, refreshScreen)
timer.start( )
//refreshScreen needs to have an event parameter
private function refreshScreen( e:TimerEvent ):void
{
refresh(null);
}
Related
I have a child component that dispatches an event in Parent. The event in parent makes a call to our database. Right now, the event gets fired off & the child continues without the results. How do I make it so that the child waits for the results from the database b/f the child continues?
in child:
<fx:Script>
<![CDATA[
dispatchEvent(new Event("getDBcontents")); // dispatch the event in the parent
// do some more stuff here but we need pause until we get the result from the parent
]]>
</fx:Script>
in parent:
public function getDBcontents(event:Event):void {
otherChild.getResult.token = otherChild.childRet.getContents( 'userID.text' );
}
Move the "// do some more stuff here but we need pause until we get the result from the parent" section to a different part. I assume that you are doing a remote call to your database that that has a callback. I'm not sure which mechanism you're using, but let's assume a RemoteObject.
You can pass a function on a custom event that you dispatch. The database section of your code can attach that function pointer to the AsyncToken or just add it to the class instance. Then when it comes back with results you can then call the function that you had passed in as part of the event. The joys of async programming.
I'd recommend looking at the patterns used in Cairngorm and Swiz (Swiz being my preferred framework) as the way they do database calls in those frameworks is exactly what you're trying to do here.
As an example, you could do something like this:
dispatchEvent(new MyCustomEvent("getDBcontents", callBackFunction));
private function callBackFunction(stuffToProcess:Object):void {
//do more stuff here after the stuff is returned
}
//first create MyCustomEvent class extending Event
//Then you need something to handle the event, you can build the event listener yourself, or use something like Swiz to make your life easier
//here is your event handler that you can call yourself, or assign through Swiz Cairngorm
var st:Function;
public myEventHandler(event:MyCustomEvent):void {
st = event.callBackFunction; //your param on your custom function
var token:AsyncToken=this.service.doSomething();
var responder:mx.rpc.Responder=new mx.rpc.Responder(genericResultsHandler, faultHandler);
token.addResponder(responder);
}
genericResultsHandler(result:ResultEvent):void{
if (st != null)
st(result.data);
}
In Flex Air app, how do you open a window behind an active one?
I tried following and i can't seem to get it to work
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="onCreationComplete(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
import spark.components.Window;
private var window1:Window = new Window();
private var window2:Window = new Window();
private var timer:Timer = new Timer(3000,1);
private function onCreationComplete(event:FlexEvent):void
{
window1 = new Window();
window1.title = "Window 1";
window1.width = 200;
window1.height = 200;
window1.open(false);
window1.orderInBackOf(this);
window2 = new Window();
window2.title = "Window 2";
window2.width = 200;
window2.height = 200;
timer.addEventListener(TimerEvent.TIMER_COMPLETE, openWindow2, false, 0, true);
timer.start();
}
private function openWindow2(event:TimerEvent):void
{
window2.open(false);
window2.orderInBackOf(window1);
}
]]>
</fx:Script>
</s:WindowedApplication>
With this code, I would expect window1 to open behind the main app window and, in 3 seconds, window2 would open behind window1. But if you execute this, window1 will open on top of the main window and window2 will open on top of window1 and the main app will retain focus. This seems like a bug in the Flex. If so is there any workaround for this problem?
So it seems like there isn't a good way to solve this problem. A workaround would be to add event listener for AIREvent.WINDOW_COMPLETE and move the window in the event handler. This seems like "correct" workaround and it works for a simple case like my original example. Problem is that this approach did not work on my code.
If you look at the spark.components.Window code from the sdk, you will see that an event listener for Event.ENTER_FRAME is added when opening the window (in commitProperties()). Then in the event handler, enterFrameHandler(), it keeps a counter, and on second frame, dispatch AIREvent.WINDOW_COMPLETE event. This makes me think that AIREvent.WINDOW_COMPLETE event will fire on second frame regardless of the status of the window even though the livedoc says:
Dispatched when the Window completes its initial layout and opens the underlying NativeWindow.
I'm guessing my window wasn't full created by the second frame and therefore orderInBackOf failed.
So here's my workaround:
First, in my window, I override open(openWindowActive). If openWindowActive is false, I add an event listener for Event.ENTER_FRAME. In the event handler, I keep a counter and, when the frame count reaches a certain number (10 right now), I move the current window behind the active one. This is a VERY hacky way to get around this problem. I've been increasing the limit and now it succeeds about 90% of the time. I could continue to increase the limit until I get close to 100% but I hate having to check this condition every time I make any changes to the window. Also this condition could be different depending on the machine.
I would LOVE it if someone can tell me how wrong my workaround is and tell me a much much better way to solve my problem... but till then this will have to do...
Is there a particular reason you're setting the useWeakReference parameter to true? Otherwise, you should be able to just call:
timer.addEventListener(TimerEvent.TIMER_COMPLETE, openWindow2);
However, there's a bug in that line of code. The TIMER_COMPLETE event is fired when the timer is finished all its cycles. You have set your timer to "fire" infinitely. So it will never complete it's cycles.
You need to modify that line of code to the following and you should get your expected results:
timer.addEventListener(TimerEvent.TIMER, openWindow2);
To address your second question (why window1 doesn't appear behind the application). Judging by the return value of the orderInBackOf function:
Boolean — true if the window was succesfully sent behind;
false if the window is invisible or minimized.
It seems that ordering fails if the the window is invisible. It may be the case that, by having the code in the creationComplete handler, you're calling this function before the Application window has a chance to display itself. Try moving that code within your openWindow2 function, as well. Yeilding:
private function openWindow2(event:TimerEvent):void
{
window1.orderInBackOf(this);
window2.open(false);
window2.orderInBackOf(window1);
}
I hope this helps in some way,
gMale
EDIT: per my last comment try this,
private function openWindow2(event:TimerEvent):void
{
window1.depth = 5; //arbitrary number
window2.depth = 4;
window1.open(false); //assumes window1 isn't opened before
window2.open(false);
}
I am developing a static flex application which does not have a database connection, all the values are hardcoded(its just a prototype for the original app). Now when i click the save button, i need to get a message like saving in progress... please wait, I need to display this message for 3 seconds.
Please let me know how could this be done.
Thanks!
Cheers,
Deena
I am putting in the complete code of how i did it for easy reference to other users.
First Create a savingProgressBar.mxml file with the progress bar with the required format.
Then in the parent page where you want the progress bar enter the following scrip code
[In my parent page i click a button called save and on click of it i am calling the save() function]
private var pBar:IFlexDisplayObject;
private function save()
{
pBar=PopUpManager.createPopUp( this, savingProgressBar, true);
PopUpManager.centerPopUp(pBar);
var myTimer:Timer = new Timer(1500,1)
myTimer.addEventListener(TimerEvent.TIMER, timerHandler);
myTimer.start();
}
public function timerHandler(event:TimerEvent):void
{
PopUpManager.removePopUp(pBar);
}
]]>
Hope this helps,
cheers,
Deena
Fist create a timer with:
private var t:Timer = new Timer(3000,1);
Then add an event lister to resond when the timer will be finished:
t.addEventListener(TimerEvent.TIMER_COMPLETE, removeMSG);
//start timer
t.start();
Add, removeMSG function that will remove your progress bar or notifier:
private function removeMSG(e:TimerEvent):void{
//code to remove the notification
}
Also if you plan to use ProgressBar control in Flex use indeterminate="true" that will make progress bar move without any feedback data from your webservice
Use a timer that triggers every 200ms or something, and each time it triggers have it add 3s/200ms to the progress bar.
I am using a Tree control with an XMLListContainer dataProvider.... I use an itemOpen event with the following code to update another data provider when a tree folder is opened (using small triangle) - the data provider contains all the <slide /> elements in that particular tree folder...
private function itemOpenEvent(event:TreeEvent):void {
slideDP = new XMLListCollection(event.item.elements("slide"));
slideDP.refresh();
}
If a second folder is opened thumbDP updates fine but when the first folder (or another closed folder) is clicked I want the same behaviour to happen (currently you have to close and reopen the first folder)
So I use a itemClick event - but this fires a ListEvent and I can't work out how to get the child elements from the XMLListContainer as easy... The code below throws an out of bounds exception
private function itemClickEvent(event:ListEvent):void {
treeFeed.getItemAt(event.rowIndex);
}
Can anyone help? Thanks :)
I would change your event listener to listen for a change Event, and use the selectedItem property of the Tree:
private function changeHandler(event:ListEvent):void
{
slideDP = new XMLListCollection(tree.selectedItem.elements("slide"));
slideDP.refresh();
}
You may need to cast selectedItem as XML or XMLList.
How can I open a synchronous dialog in Flex? I need to call a function from an External Interface (JavaScript) that will open a simple dialog in the Flex application and returns an value according to the button the user has clicked (OK/Cancel).
So it should by a synchronous call to a dialog, i.e. the call waits until the user has closed the dialog like this.
//This function is called by JavaScript
function onApplicationUnload():Boolean
{
var result:Boolean;
result = showDialogAndWaitForResult();
return result
}
Does anybody know how I can do this? I could write a loop that waits until the dialog has set a flag and then reads the result to return it, but there must be something that is way more elegant and reusable for waiting of the completion of other asynchronous calls.
EDIT:
Unfortunately a callback does not work as the JavaScript function that calls onApplicationUnload() itself has to return a value (similar to the onApplicationUnload() function in Flex). This JavaScript function has a fixed signature as it is called by a framework and I cannot change it. Or in other words: The call from JavaScript to Flex must also be synchronous.
Flex doesn't work in a synchronous fashion, as it is a single thread application and so needs your code to hand execution back to the "core" in order to handle user input etc.
The way to do it is to make your dialogue's behaviour asynchronous:
function onApplicationUnload():void
{
showDialog(resultMethod);
}
function resultMethod(result:Boolean):void
{
ExternalInterface.call("javaScriptCallback", [result]);
}
You can't do that in Flex. As David mentioned, Flex is single-threaded, so you can't have your function block while the dialog is being processed.
Your best bet might be to use a Javascript popup. You'll have a lot less control over the window, but it should behave the way you want (blocking the function until it's been closed).
Have your Flex code use an event to wait for the dialog. In the main thread, register an event handler that waits for the dialog to close. On OK in the dialog, dispatch the dialog complete event.
With Cairngorm, this is something like:
In the main thread:
CairngormEventDispatcher.getInstance().addEventListener(ClosingDialogCompleteEvent.DIALOG_COMPLETE, onClosingDialogComplete);
(if you want to avoid returning until complete, loop on a timer and global variable.)
In the dialog closing handler:
CairngormEventDispatcher.dispatchEvent(new ClosingDialogCompleteEvent(<parameters>));
The event handler:
public function onClosingDialogComplete (e: ClosingDialogCompleteEvent):void
{
param1 = e.param1;
param2 = e.param2;
// etc.
// Continue processing or set the global variable that signals the main thread to continue.
}
For this to work, the class ClosingDialogCompleteEvent has to be defined. Partial code for the class is:
package com. ... .event // You define where the event lives.
{
import com.adobe.cairngorm.control.CairngormEvent;
public class ClosingDialogCompleteEvent extends CairngormEvent
{
// Event type.
public static const DIALOG_COMPLETE:String = "dialogComplete";
public var param1:String;
public var param2:String;
public function ClosingDialogCompleteEvent(param1:String, param2:String)
{
super(DIALOG_COMPLETE);
this.param1 = param1;
this.param2 = param2;
}
}
}
Waiting on an event is the best way to synchronize in Flex. It works well for startup dialogs too. In a flex-only application it works especially well.
I have explained a workaround to create synchronous alert in flex
http://reallypseudorandom.blogspot.com/2010/05/flash-asynchronous-alert-and-pause.html
OK... after all I found a possible solution. But I guess hardly everybody is going to do that seriously :-(
The solution focuses around using a while loop to check for a result and then return the function that is being called by JavaScript. However we need a way to sleep in the while loop, while we are waiting for the result. However calls to JavaScript are synchronous. Now the trick is to make a sleep in JavaScript, which is also not directly available here, but can be done using a synchronous XML Http Request like described on this blog.
As I said - I won't recommend this only as last resort. For my problem I have resorted to ugly JavaScript popups.
Have your dialog call another function in flex to process the result of the user selection:
private function deleteFileCheck():void
{
Alert.show("Are you sure you want to delete this file?",
"Confirm Delete",
Alert.YES| Alert.NO,
this, deleteFileHandler, null, Alert.NO);
}
private function deleteFileHandler(event:CloseEvent):void
{
if (event.detail == Alert.YES)
{
...do your processing here
}
}
You can fake a synchronous dialog in flex by popping up a dialog then disabling everything in the background. You can see this in action if you do Alert.show("Hello World"); in an application. The background will grey out and the user won't be able to click on any UI in the background. The app will "wait" until the user clicks the OK button.