So basically I have a component with my event dispatched:
<components:MyComp id="Id" myDispatchedEvent(event)/>
In script tags I have that function:
private function myDispatchedEvent(event:Event):void
{
//Here I have my static function with title and handler function showConfirmation
Calculate.showConfirmation("String Title", function(event:Close):void
{
if(bla bla bla)
//lots of code etc. ...
});
//myDispatchEvent function continues here..
}
So problem is with my static function's showConfirmation handler, if I go through debug, it just skips that function and continues doing myDispatchedEvent. Why doesn't anonymous function inside showConfirmation function execute?
Thanks
Functions are executing upon call. In your case you have just declaration of it. Call this function somewhere inside Calculate.showConfirmation and it will be executed.
Something like the following:
public class Calculate
{
public static function showConfirmation(title:String, func:Function):void
{
// The call I'm talking about is here
func(new Close());
}
}
Let me say first that what you're trying to do is quite strange. I'd try to code a different solution, but this depends on what you're trying to do. It you tell us a more about it we could find a better way to reach your goal. BTW, you can do something like this:
<?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:mx="library://ns.adobe.com/flex/mx"
minHeight="600" minWidth="955">
<fx:Script>
<![CDATA[
import mx.events.CloseEvent;
public static function myFunction(param:String, func:Function):void {
trace("executing");
func.apply();
}
protected function labelx_clickHandler(event:MouseEvent):void {
trace("click");
Tests.myFunction("Test", function():void {
if (event.localX > 0) {
trace("Test");
}
else {
trace("No");
}
});
}
]]>
</fx:Script>
<s:Button id="labelx"
label="Click me"
click="labelx_clickHandler(event)"/>
</s:Application>
Something similar what Constantiner has already told you. If you don't execute the function that you're passing to your static function as a parameter inside this static function, it won't be executed.
Related
Inspired by the lazy loading abilities of Hibernate I wanted to make the model part of my Flex UI request data from the server only when necessary. I thought this would be as simple as adding a public accessor that only sends server requests when the variable is accessed.
public function get tab2AC():ArrayCollection
{
if(_tab2AC == null){
//Request data from server
}
return _tab2AC;
}
Problem is that Flex seems to access all bound variables on application launch, even if the referencing component has yet to be created. So even though the DataGrid with dataProvider="{tab2AC}" has yet to be created, the server request still goes out, thus defeating the "only when required" laziness.
I do not want to place the server request inside a creationComplete handler as I want to keep my UI model ignorant of view state and my view ignorant of server requests.
Interestingly, if I add an Alert.show("anything"); inside the accessor, it works as desired.
UPDATE: Here is a full example. Set breakpoints and you'll see that Flex accesses both variables even though titleForScreen2 is not used by any created component.
<?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:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">
<fx:Script>
<![CDATA[
private var _titleForScreen1:String;
private var _titleForScreen2:String;
public function get titleForScreen1():String {
if(_titleForScreen1 == null){
//Server Request
}
return _titleForScreen1;
}
public function get titleForScreen2():String {
if(_titleForScreen2 == null){
//Server Request
}
return _titleForScreen2;
}
]]>
</fx:Script>
<mx:ViewStack>
<s:NavigatorContent label="Screen 1">
<s:Label text="{titleForScreen1}"/>
</s:NavigatorContent>
<s:NavigatorContent label="Screen 2">
<s:Label text="{titleForScreen2}"/>
</s:NavigatorContent>
</mx:ViewStack>
</s:Application>
Bindings in flex are pretty stupid. More of a proof of concept than an actual optimized production quality feature. What's worse is that short of modifying the compiler, there's little you can do about it without having all sorts of verification logic in your getter or (perhaps more likely) some kind of interceptive layer that makes sure that expensive calls are only made when the UI state is meaningful. At that point however, you might as well do away with bindings altogether and just implement an active controller for a passive view.
I know this is a pretty lame answer, but it's true. I've been a flex developer for several years and have had a complicated relationship with its binding feature for just as long. As well, over all this time, the only thing that has changed in the binding implementation is the ability to do two-way bindings.
By the way, syntactically I'd use a regular method rather than a property for returning a promise. Properties are often read as synchronous and cheap(-ish) operations, whereas a method (especially one that returns a promise) would have more flexible connotations.
<?xml version="1.0" encoding="utf-8"?>
<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" minWidth="955" minHeight="600">
<fx:Script>
<![CDATA[
import mx.controls.Alert;
private var _titleForScreen1:String;
private var _titleForScreen2:String;
public function get titleForScreen1():String {
if(_titleForScreen1 == null){
//Server Request
}
return _titleForScreen1;
}
public function get titleForScreen2():String {
Alert.show("test");
if(_titleForScreen2 == null){
//Server Request
}
return _titleForScreen2;
}
]]>
</fx:Script>
<mx:ViewStack>
<s:NavigatorContent label="Screen 1">
<s:Label text="{titleForScreen1}"/>
</s:NavigatorContent>
<s:NavigatorContent label="Screen 2">
<s:Label text="{titleForScreen2}"/>
</s:NavigatorContent>
</mx:ViewStack>
</s:WindowedApplication>
Breakpoints on lines 12 and 19, check out the stack trace during each, also you can pop open Binding and take a look at wrapFunctionCall (drop a breakpoint in there too). So when it gets to lines 12 and 19, it hits them 2 times because of the preloader dispatching a complete event. I put breakpoints in every file that the stack-trace showed the execution path moving through. Unfortunately I couldn't find the point where it caused 2 calls to happen (must be in the parts I don't have the source for) it seemed every spot in the trace was only called once but I think the wrapFunctionCall was called twice during the period of those two executions. The third one that happens is due to a call to doPhasedInstantation which calls down to execute on Bindings for all the children that have the systemManager, so it would seem somehow the components have a system manager even though they may have not yet been added to the stage or created. Sorry I don't have a more concrete answer for why each of these has to happen but my guess is there's some good reason.
Ah yah almost forgot, also you'll see when the Alert is shown it causes an error but that error is captured in the wrappedFuncitonCall method in Binding.as
catch(error:Error)
{
// Certain errors are normal when executing a srcFunc or destFunc,
// so we swallow them:
// Error #1006: Call attempted on an object that is not a function.
// Error #1009: null has no properties.
// Error #1010: undefined has no properties.
// Error #1055: - has no properties.
// Error #1069: Property - not found on - and there is no default value
// We allow any other errors to be thrown.
if ((error.errorID != 1006) &&
(error.errorID != 1009) &&
(error.errorID != 1010) &&
(error.errorID != 1055) &&
(error.errorID != 1069))
{
throw error;
}
else
{
if (BindingManager.debugDestinationStrings[destString])
{
trace("Binding: destString = " + destString + ", error = " + error);
}
}
}
Your statement is not true, tab2AC getter is not accessed by Flex app on launch, as a proof here is the full application code:
<?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:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
private var _tab2AC:ArrayCollection;
public function set tab2AC(value:ArrayCollection):void
{
_tab2AC = value;
}
[Bindable]
public function get tab2AC():ArrayCollection
{
if(_tab2AC == null){
trace("THIS WILL NOT BE CALLED");
}
return _tab2AC;
}
]]>
</fx:Script>
</s:Application>
As you can see, the trace will not be triggered, so your problem seems to be coming from a call to that getter from somewhere in your app, to find it, put a break-point and then "Step return" when needed.
That being said, you should not implement the lazy loading this way directly in the getter as the service call is asynchronous.
Why not return back a new ArrayCollection when the variable is null, then set the source on the ArrayCollection when the server call returns?
I think this is just wonky behavior of the debugger, rather than what would happen in ordinary execution. If you put any logic in there that will enable you to determine that the function was called that doesn't tie into the debugger (such as setting the text on a label), then the getter doesn't get called. However, if you put a trace statement in there, the getter does get called.
The conundrum is how much do you want to depend on the idea that this is only going to happen in debugging, and how critical is it to get the real behavior while you are using the debug player?
So yeah, that's just the way it is. Since Flex evaluates bindings immediately, I have to delay bindings until creation in order to prevent premature evaluation. Seems like extra work to undo Flex's weird behavior, but that's just how it goes sometimes.
<?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:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">
<fx:Script>
<![CDATA[
import mx.binding.utils.BindingUtils;
import mx.binding.utils.ChangeWatcher;
private var _titleForScreen1:String;
private var _titleForScreen2:String;
public function get titleForScreen1():String {
if(_titleForScreen1 == null){
//Server Request
}
return _titleForScreen1;
}
public function get titleForScreen2():String {
if(_titleForScreen2 == null){
//Server Request
}
return _titleForScreen2;
}
public function updateLabel1(value:String):void {screen1Label.text = value;}
public function updateLabel2(value:String):void {screen2Label.text = value;}
public function bindLabel1():void {
var changeWatcher:ChangeWatcher = BindingUtils.bindSetter(updateLabel1,this, "titleForScreen1");
}
public function bindLabel2():void {
var changeWatcher:ChangeWatcher = BindingUtils.bindSetter(updateLabel2,this, "titleForScreen2");
}
]]>
</fx:Script>
<mx:ViewStack>
<s:NavigatorContent label="Screen 1">
<s:Label id="screen1Label" creationComplete="bindLabel1()"/>
</s:NavigatorContent>
<s:NavigatorContent label="Screen 2">
<s:Label id="screen2Label" creationComplete="bindLabel2()"/>
</s:NavigatorContent>
</s:NavigatorContent>
</mx:ViewStack>
</s:Application>
When I test my deployed app in a browser the popup window continues to be displayed even after it should be closed. Everything works as expected when debugged in Flash Builder 4.
Following is currently what's happening: the request is sent to my restful web service, which processes the request, (seemingly) the ResultEvent is called which in turn dispatches the profileEvt dynamic event that changes the view state. However, the popup window does not get closed and the applet gets 'stuck.'
Anyone know what could be the problem? Below are the flex applet web service event listeners/handlers:
webService.addEventListener(ResultEvent.RESULT, function(event:ResultEvent):void
{
var rawData:String = String(event.result);
var profileEvt:DynamicEvent = new DynamicEvent("profileSaved", true);
profileEvt.data = JSON.decode(rawData).profile;
dispatchEvent(profileEvt); // Dispatch profile saved event
_progressPopUp.closePopUp();
dispatchEvent(event); // Dispatch submit profile button clicked
});
webService.addEventListener(FaultEvent.FAULT, function(event:FaultEvent):void
{
Alert.show("Could not create profile; please try again later.\n" + event.message, "Status");
_progressPopUp.closePopUp();
});
var params:Object = {"profile" : profile};
try
{
_progressPopUp = PopUpManager.createPopUp(this, com.profs.ui.components.ProgressPopUp, true);
_progressPopUp.eventSource = webService; // Set source of progress events
webService.send(JSON.encode(params));
}
NOTE:
com.profs.ui.components.ProgressPopUp is a custom component; the code for it is below:
<?xml version="1.0" encoding="utf-8"?>
<mx:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" layout="absolute" width="300" height="200" showCloseButton="false" title="Status" creationComplete="init()">
<fx:Declarations></fx:Declarations>
<fx:Script>
<![CDATA[
import mx.managers.PopUpManager;
[Bindable] public var eventSource:Object;
private function init():void
{
PopUpManager.centerPopUp(this);
}
public function closePopUp():void
{
PopUpManager.removePopUp(this);
}
public function completionHandler(event:Event):void
{
closePopUp();
}
]]>
</fx:Script>
<mx:ProgressBar id="progressBar" indeterminate="true" mode="event" source="{eventSource}" complete="completionHandler(event)" verticalCenter="0" horizontalCenter="0"/>
</mx:TitleWindow>
I am not familiar with the com.profs.ui.components.progressPopUp component, but it is possible that the closePopUp() method has a bug in it. You could try to remove the ProgressPopUp directly using the PopUpManager method. For example instead of:
_progressPopUp.closePopUp();
try
PopUpManager.removePopUp(_progressPopUp);
I also don't know off the top of my head what the rules for closures are (i.e. at which point is the _progressPopUp variable copied into the ResultEvent.RESULT event handler. You could try moving that particular event handler below the line where you actually created the _progressPopUp instance.
I'm developing an AIR application, where i need to access
WindowedApplication's function from the package class.
This is the Main application (Partial code)
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute" creationComplete="initApplication()">
<mx:Script>
<![CDATA[
import mx.events.CloseEvent;
import messages.MessageWindow
public function undock():void {
stage.nativeWindow.visible = true;
stage.nativeWindow.orderToFront();
//Clearing the bitmaps array also clears the applcation icon from the systray
NativeApplication.nativeApplication .icon.bitmaps = [];
}
]]>
</mx:Script>
</mx:WindowedApplication>
Package: (Partial code)
package messages
{
public class MessageWindow extends NativeWindow
{
public function MessageWindow():void
{
stage.addEventListener(MouseEvent.MOUSE_DOWN,onClick);
}
private function onClick(event:MouseEvent):void
{
****** Need to call the undock method from here. *****
}
}
}
Is it possible to call this way or suggest any other solution
Thanks in advance
Senling.
Even if I don't recommend this for the sake of your code design, you can access your method like this:
Application.application.undock()
(if your undock() method is public in the WindowedApplication )
Cant see why it shouldnt work. Go ahead and give it a try, and if any errors come up, post it here.
What you can maybe try is to add parentApplication in front of stage in the MessageWindow method, like this..
parentApplication.stage.addEventListener(MouseEvent.MOUSE_DOWN,onClick);
and then call the undock() method from the onClick() method
I have several components where I want to enable buttons based on passing a username to a function. I want to dynamically bind the "enabled" property on a button so that if the "somethingChanged" event fires, a button may become enabled or disabled.
But, I'm not sure where to fire the "somethingChanged" event. It's possible that I may need to fire the "somethingChanged" event from several places in the application. Is this possible with a bound static function?
Am I going about this the right way or is there a better solution?
EventManager.as
public class EventManager():void
{
[Bindable(event="somethingChanged")]
public static function hasAccess(myVal:String):Boolean
{
}
}
testform1.mxml
<s:Button id="testButton1" enabled="{EventFunction.hasAccess("john")}" />
<s:Button id="testButton2" enabled="{EventFunction.hasAccess("mary")}" />
<s:Button id="testButton3" enabled="{EventFunction.hasAccess("beth")}" />
testform2.mxml
<s:Button id="testButton4" enabled="{EventFunction.hasAccess("tom")}" />
<s:Button id="testButton5" enabled="{EventFunction.hasAccess("bill")}" />
<s:Button id="testButton6" enabled="{EventFunction.hasAccess("jerry")}" />
Something like this would work.
package
{
import flash.events.Event;
import flash.events.EventDispatcher;
public class EventManager extends EventDispatcher
{
[Bindable(event="somethingChanged")]
public var hasAccess:Boolean = true;
public static var instance:EventManager = new EventManager();
public static function setHasAccess(hasAccess:Boolean):void
{
instance.hasAccess = hasAccess;
instance.dispatchEvent(new Event("somethingChanged"));
}
}
}
Which would be used like so:
<?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:mx="library://ns.adobe.com/flex/halo">
<fx:Script>
<![CDATA[
protected function button1_clickHandler(event:MouseEvent):void
{
EventManager.setHasAccess( false );
}
]]>
</fx:Script>
<mx:Button click="button1_clickHandler(event)" enabled="{EventManager.instance.hasAccess}"/>
</s:Application>
I don't know that this approach is a good one. This type of functionality would probably be better served inside a proper MVC structure. This is essentially creating a singleton. To implement it as you describe, you'd probably want some sort of instance per user I suppose. You aren't going to be able to bind to a static function though.
In certain cases, I can't seem to get components to receive events.
[edit]
To clarify, the example code is just for demonstration sake, what I was really asking was if there was a central location that a listener could be added, to which one can reliably dispatch events to and from arbitrary objects.
I ended up using parentApplication to dispatch and receive the event I needed to handle.
[/edit]
If two components have differing parents, or as in the example below, one is a popup, it would seem the event never reaches the listener (See the method "popUp" for the dispatch that doesn't work):
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
initialize="init()">
<mx:Script>
<![CDATA[
import mx.controls.Menu;
import mx.managers.PopUpManager;
// Add listeners
public function init():void
{
this.addEventListener("child", handleChild);
this.addEventListener("stepchild", handleStepchild);
}
// Handle the no pop button event
public function noPop(event:Event):void
{
dispatchEvent(new Event("child"));
}
// Handle the pop up
public function popUp(event:Event):void
{
var menu:Menu = new Menu();
var btnMenu:Button = new Button();
btnMenu.label = "stepchild";
menu.addChild(btnMenu);
PopUpManager.addPopUp(menu, this);
// neither of these work...
this.callLater(btnMenu.dispatchEvent, [new Event("stepchild", true)]);
btnMenu.dispatchEvent(new Event("stepchild", true));
}
// Event handlers
public function handleChild(event:Event):void
{
trace("I handled child");
}
public function handleStepchild(event:Event):void {
trace("I handled stepchild");
}
]]>
</mx:Script>
<mx:VBox>
<mx:Button label="NoPop" id="btnNoPop" click="noPop(event)"/>
<mx:Button label="PopUp" id="btnPop" click="popUp(event)"/>
</mx:VBox>
</mx:Application>
I can think of work-arounds, but it seems like there ought to be some central event bus...
Am I missing something?
Above is correct.
You are dispatching the event from btnMenu, but you are not listening for events on btnMenu - you are listening for events on the Application.
Either dispatch from Application:
dispatchEvent(new Event("stepchild", true));
or listen on the btnMenu
btnMenu.addEventListener("stepchild",handleStepChild);
btnMenu.dispatchEvent(new Event("stepchild",true));
You are attaching the listener to this when the event is getting dispatched from btnMenu.
This should work:
dispatchEvent(new Event("stepchild", true));
ps. There is really no reason to put an unnecessary 'this' everywhere, unless it's explicitly required to overcome scope issues. In this case you can just leave every this out.