Flex: Why can't I handle certain events? - apache-flex

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.

Related

"FlexGlobals.topLevelApplication.addEventListener" can't catch keyboard input on PopUp

Suppose I have an application and a global event listener in it. Should the key events, who are fired in the PopUp, be caught by that listener? Or maybe popups are not placed in that hierarchy?
Here's simplified test-code for you to understand what I'm talking about:
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()">
<mx:Script><![CDATA[
private function init():void {
FlexGlobals.topLevelApplication.addEventListener(KeyboardEvent.KEY_DOWN, myKeyDownHandler);
}
private function myKeyDownHandler(event:KeyboardEvent):void {
Alert.show("It works!");
}
private function makeNewPopup():void {
var win:PopupWindow = new PopupWindow(this, new TestingForm(), true, "Popup", false);
win.showPopup();
}
]]></mx:Script>
<mx:VBox>
<mx:TextInput/>
<mx:Button label="MakePopup" click="makeNewPopup()"/>
</mx:VBox>
</mx:Canvas>
Ok, what we have .. after running the application, put the input focus into the TextInput and press any letter. The Alert will be fired. Now, press the MakePopup and do the same in it TextInput .. no any feedback from it.
Any thoughts about that?
The parent of all popups is SystemManager. So, use FlexGlobals.topLevelApplication.systemManageror stage.

ButtonBarButton custom rollover/out event

What is the best way to add a custom rollover and rollout to a buttonbarbutton? I tried adding the eventlisteners in the custom skin I am using but the mouseevents do not get fired.
Any ideas?
Thanks!
Ok, I won't say this is the best way to do it by any means, but I do have a solution.
In the ButtonBar Skin I made I add a listener for the creationComplete of the DataGroup and then add a listener to each of the elements inside it.
<fx:Script>
<![CDATA[
import com.mattel.styleguide.events.DesignDropDownEvent;
import mx.controls.Alert;
private function init():void
{
for (var i:int = 0; i < dataGroup.numElements; i++)
{
dataGroup.getElementAt(i).addEventListener(MouseEvent.ROLL_OVER, onRollOver);
dataGroup.getElementAt(i).addEventListener(MouseEvent.ROLL_OUT, onRollOut);
}
}
private function onRollOver(e:MouseEvent):void
{
//dispatch custom event
}
private function onRollOut(e:MouseEvent):void
{
//dispatch custom event
}
]]>
</fx:Script>
<s:DataGroup id="dataGroup" width="100%" height="100%" creationComplete="init()">
<s:layout>
<s:ButtonBarHorizontalLayout gap="-1"/>
</s:layout>
</s:DataGroup>
This allows the rollover/rollout events to get hit for each button, I can then check the e.currentTarget and see which button was interacted with and dispatch a custom event inside the functions which bubble up to my view.
I was hoping the was a cleaner way to do it and that could very well be the truth and I just don't know how to do it. Any one else have a different idea?
Thanks!

Deployed flex applet not processing web service results

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.

A question on capturing the button click event in ANOTHER mxml file

this seems to be an interesting question to be discovered in Flex.
I registered a very simple button event listener in A.mxml:
<mx:Script><![CDATA[
import mx.controls.Alert;
public function Handler():void
{
Alert.show('click event occured');
}
]]></mx:Script>
<mx:Button label="{resourceManager.getString('resources', 'button.startLab')}"
id="nextStepButton" click="Handler()" />
It works fine when clicking the button everytime.
Now I want to have something interesting,that is,I want to capture this buttonClick Event in another mxml file,say B.mxml and do something in B.mxml instead of A.
I am bit stuck on this,hope you could give me some hint and help,thanks a lot.
There are a number of approaches to this problem. The simplest (and least object-oriented) is to have A be aware of B, or vice versa. In that case you can just add a listener. In B you could say a.nextStepButton.addEventListener(MouseEvent.CLICK, myHandler), or in A you could do this.nextStepButton.addEventListener(MouseEvent.CLICK, b.myHandler). (When one component is instantiated, you have to set a reference to it on the other component.)
One step better would be to dispatch a custom event that bubbles, with one of the components still aware of the other. In B: a.addEventListener(CustomNavigationEvent.NEXT_CLICK, myHandler), or in A: b.addEventListener(CustomNavigationEvent.NEXT_CLICK, myHandler).
Taking it further, you could just let the event bubble to the top (the SystemManager) and add your listener to the SystemManager. This way B is not aware of A at all. In B: this.systemManager.addEventListener(CustomNavigationEvent.NEXT_CLICK, myHandler).
Taking it even further, you can implement your own version of an event broadcaster, which is just a third object that is accessible by any component, usually implemented as a singleton, that takes listener registrations and accepts event dispatches, then broadcasts that event to registered listeners.
Hope that helps.
EDIT: Here's some code for doing it the first way:
In A.mxml:
<?xml version="1.0" encoding="utf-8"?>
<s:Group 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;
public var b:B;
private function onCreationComplete(e:FlexEvent):void {
// Note that you have to have a public onClick handler in B
this.myButton.addEventListener(MouseEvent.CLICK, b.onClick);
}
]]>
</fx:Script>
<s:Button id="myButton"/>
</s:Group>
You need to make A aware of B in the container that declares instances of both A and B:
MXML:
<mypackage:A id="aComponent" b="bComponent"/>
<mypackage:B id="bComponent"/>
ActionScript equivalent:
var aComponent:A = new A();
var bComponent:B = new B();
aComponent.b = bComponent;

flex button event listeners in Class

I have many buttons in Main.mxml. I'm trying to move the button functionality into a Class and have Event Listeners inside the class respond to Click and call other functions. I have written:
Main.mxml
<mx:Button x="23.5" y="10" label="checker" click="{goListen()}" />
<mx:Button id="btnT1" x="252.5" y="10" label="t1" />
<mx:Button id="btnT2" x="309" y="10" label="t2"/>
<mx:Button id="btnT3" x="366" y="10" label="t3"/>
Main.as
private function goListen():void
{
var t:ButtonListener = new ButtonListener(btnT1, btnT2, btnT3);
}
ButtonListener.mxml
package com.util
{
import flash.events.EventDispatcher;
import flash.events.MouseEvent;
import mx.controls.Alert;
import mx.controls.Button;
public final class ButtonListener extends EventDispatcher
{
private var __btnArray:Array;
public function ButtonListener(...btnList)
{
__btnArray = new Array();
for each (var item:Button in btnList)
{
__btnArray.push(item);
}
buildListeners();
}
private function buildListeners():void
{
for each (var item:Button in __btnArray)
{
item.addEventListener(MouseEvent.CLICK, traceMe, false, 0, true);
}
}
private function traceMe(event:MouseEvent):void
{
trace(event.target.label + " was clicked");
}
}
}
so when I debug, I see the array filling up with the buttons, but the traceMe() function won't work. Not sure how I can get this to work. Or do I just have to have 30 event listeners and corresponding functions in the main class.
It looks like you have two different options or problems. If you change the last parameter in:
item.addEventListener(MouseEvent.CLICK, traceMe, false, 0, true);
to false, then everything should work because your event listener will stick around to handle the mouse clicks. Of courses, this means that if you click on your "checker" button a second time, you'll then have two sets of listeners responding to mouse clicks of buttons one, two, and three.
So, it's likely that the real solution you're interested in is leaving the line quoted above the same and instead changing the following line:
var t:ButtonListener = new ButtonListener(btnT1, btnT2, btnT3);
If you change the above line to store your button listener as a part of your class it will be available to respond to the mouse clicks, rather than having been garbage collected:
_buttonListener = new ButtonListener(btnT1, btnT2, btnT3);
That, of course, assumes that you have defined _buttonListener within an mx:script block:
<mx:Script><![CDATA[
var _buttonListener:ButtonListener;
]]></mx:Script>
EDIT per comment:
In the code provided, t, the ButtonListener, goes out of scope. When it does, it is garbage collected unless you use strong references, which you do not per the last parameter in your addEventListener call.
Thus, make the button listener a member of the main class:
Main.mxml would then read:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Button x="23.5" y="10" label="checker" click="{goListen()}" />
<mx:Button id="btnT1" x="252.5" y="10" label="t1" />
<mx:Button id="btnT2" x="309" y="10" label="t2"/>
<mx:Button id="btnT3" x="366" y="10" label="t3"/>
<mx:Script>
<![CDATA[
private var _buttonListener:ButtonListener;
private function goListen():void
{
_buttonListener = new ButtonListener(btnT1, btnT2, btnT3);
}
]]>
</mx:Script>
</mx:Application>
Since the event listener will no longer go out of scope, the weak references used by the event listeners will work as expected, being garbage collected when __buttonListener goes out of scope.
Since the click event of Button bubbles, you can just listen for a click event on the main application file and delegate to a handler function in a class.
Or you can call the handler directly on the click of your button.
private var controller:ButtonListener = new ButtonListener();
<mx:Button id="btnT1" x="252.5" y="10" label="t1" click="controller.handleClick(event)"/>

Resources