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.
Related
I have prepared a simplified test case for my question. It will run instantly in your Flash Builder if you put the 2 files below into a project.
I'm trying to display a List of strings and a confirmation checkbox in a popup:
In the real application I dispatch a custom event with the string selected in the list, but in the test code below I just call trace(str);
My problem: if I use click event, then the window closes, even if I click at a scrollbar (the !str check below doesn't help, when an item had been selected in previous use). And if I use change event, then the window doesn't close, when I click on the same item as the last time. And the itemClick event seems not to be present in spark.components.List anymore.
Any suggestions please on how to handle this probably frequent problem?
Writing a custom item renderer and having a click event handler for each item seems to be overkill for this case, because I have strings in the list.
Test.mxml: (please click myBtn few times - to see my problems with click and change)
<?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="400" minHeight="300">
<fx:Script>
<![CDATA[
import mx.managers.PopUpManager;
private var _popup:Popup = new Popup();
private function showPopup(event:MouseEvent):void {
PopUpManager.addPopUp(_popup, this, true);
PopUpManager.centerPopUp(_popup);
}
]]>
</fx:Script>
<s:Button id="myBtn" right="5" bottom="5"
label="Open window" click="showPopup(event)" />
</s:Application>
Popup.mxml:
<?xml version="1.0" encoding="utf-8"?>
<s: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"
width="240" height="240"
creationComplete="init(event)"
close="close()">
<fx:Script>
<![CDATA[
import mx.collections.ArrayList;
import mx.controls.Alert;
import mx.events.FlexEvent;
import mx.events.CloseEvent;
import mx.events.ItemClickEvent;
import mx.managers.PopUpManager;
private var myData:ArrayList = new ArrayList();
private function init(event:FlexEvent):void {
// XXX in the real app data is updated from server
myData.removeAll();
for (var i:uint = 1; i <= 10; i++)
myData.addItem('Item #' + i);
}
public function close(event:TimerEvent=null):void {
PopUpManager.removePopUp(this);
}
private function handleClick(event:MouseEvent):void {
var str:String = myList.selectedItem as String;
if (!str)
return;
if (myBox.selected) {
Alert.show(
'Select ' + str + '?',
null,
mx.controls.Alert.YES | mx.controls.Alert.NO,
null,
handleConfirm,
null,
mx.controls.Alert.NO
);
} else {
sendEvent();
}
}
private function handleConfirm(event:CloseEvent):void {
if (event.detail == mx.controls.Alert.YES)
sendEvent();
}
private function sendEvent():void {
close();
// XXX in the real app dispatchEvent() is called
trace('selected: ' + (myList.selectedItem as String));
}
]]>
</fx:Script>
<s:VGroup paddingLeft="20" paddingTop="20"
paddingRight="20" paddingBottom="20" gap="20"
width="100%" height="100%">
<s:List id="myList" dataProvider="{myData}"
click="handleClick(event)"
width="100%" height="100%" fontSize="24" />
<s:CheckBox id="myBox" label="Confirm" />
</s:VGroup>
</s:TitleWindow>
Also I wonder, why do I get the warning above:
Data binding will not be able to detect assignments to "myData".
The Spark List dispatches an 'IndexChangeEvent.CHANGE'. You can listen for this event to know when the selection in the List has changed.
<s:List id="myList" dataProvider="{myData}"
change="handleIndexChange()"
width="100%" height="100%" fontSize="24" />
That event is only dispatched whenever the selected index actually changes, which means that when you reopen the window a second time an item might still be selected and when you click on that one, no CHANGE event will be fired. To fix this just deselect the selection before you close the window:
public function close():void {
myList.selectedIndex = -1;
PopUpManager.removePopUp(this);
}
Also make sure to dispatch your event with the selected item before you close the window (and deselect it).
As for your question about the binding warning: you get that message because you didn't mark 'myData' to be bindable. To fix this just use the [Bindable] tag:
[Bindable]
private var myData:ArrayList = new ArrayList();
or skip the binding altogether if you don't need it and just assign the dataprovider to the list in ActionScript:
myList.dataProvider = myData;
I'd recommend two solutions if you absolutely wnat to display what item was selected. Otherwise, the solution provided by RIAStar would do the trick.
Listen to rendererAdd and rendererRemove events within your PopUp
As explained here, you can easily access to your list's renderers without interfering with its virtualLayout business.
Use a custom renderer
I know. But as long as you keep your code clean, itemRenderers won't blow up your application's memory. They're made to render huge amount of items without memory leaks.
In your Test.mxml, modify the codes like:
<fx:Script>
<![CDATA[
import mx.managers.PopUpManager;
private var _popup:Popup;
private function showPopup(event:MouseEvent):void {
_popup = new Popup();
PopUpManager.addPopUp(_popup, this, true);
PopUpManager.centerPopUp(_popup);
}
]]>
</fx:Script>
And in your Popup.mxml, I am not sure why you have the TimerEvent in the close function.
Also the trace won't be shown, as you are calling the close() function immediately after the alert's YES button has been clicked..
Rather than deal with trying to figure out the passing of parameters to an embedded SWF, I went ahead and made 20 SWF's all compiled with different values. These SWF's are now inside my Flash Builder application.
There is a state for each SWF (using includeIn) so when the user clicks the button to switch states, the appropriate SWF is displayed.
The problem is that when a user views an SWF, it remains loaded and running in the background. I would like to unload the viewed SWF when the user leaves the state and then load it when it is needed again. If this is not possible, then I will settle for simply reloading the SWF when the state is entered, and just leave the other 19 running in the background.
I have the following:
<fx:Script><![CDATA[
private var flashMovie1:MovieClip;
private var flashMovie2:MovieClip;
private function initFirst():void{
flashMovie1 = dmp_first.content as MovieClip;
}
private function initSecond():void{
flashMovie2 = dmp_second.content as MovieClip;
}
protected function btnFirst_clickHandler():void
{
flashMovie2.Stop();
currentState='First';
flashMovie1.Play();
}
protected function btnSecond_clickHandler():void
{
flashMovie1.Stop();
currentState='Second';
flashMovie2.Play();
}
]]></fx:Script>
<mx:SWFLoader id="dmp_first" includeIn="First" source="assets/images/dmp_first.swf" complete="initFirst()"/>
<mx:SWFLoader id="dmp_second" includeIn="Second" source="assets/images/dmp_second.swf" complete="initSecond()"/>
Along with the above code not working at all with the Stop and Play, I still can't figure out how to force an SWF to reload. Any help would be greatly appreciated!
You can have only one swf at a time using a conatiner. Also you donlt have to use states :) for ex:
<?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">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
import mx.controls.SWFLoader;
private function onAppCreationComplete():void
{
showSWF(0);
}
private function onSwfComboChange():void
{
showSWF(cbxSwfData.selectedIndex);
}
private function showSWF(index:int):void
{
grpContainer.removeAllElements();
var swfLoader:SWFLoader = new SWFLoader();
grpContainer.addElement(swfLoader);
swfLoader.load("assets/swfs_files/swf"+index+".swf");
}
]]>
</fx:Script>
<s:ComboBox
id = "cbxSwfData"
dataProvider = "{['swf1','swf2','swf3']}"
change = "onSwfComboChange()"/>
<s:Group
id = "grpContainer"
width = "50%"
height = "50%"/>
</s:Application>
I'm trying to communicate between a flex 4.1 application to a flash action script 2 application using LocalConnection.
flash application
contains a button called btn01 and the following code:
var a:LocalConnection = new LocalConnection();
btn01.onPress = function() {
trace("button clicked");
a.send("abcde","test");
}
you can see here that it sends a test command to the connection named 'abcde'.
flex application
<?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" initialize="init()">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.controls.Alert;
private function init():void {
var a:LocalConnection = new LocalConnection();
a.client=this;
a.connect("abcde");
}
public function test():void {
Alert.show("test");
}
]]>
</fx:Script>
<mx:SWFLoader source="/location/as2-flash-file.swf" />
as you can see, in the flex application i connect to LocalConnection named 'abcde' and i set the client to 'this' which means that all the public functions can be executed from the LocalConnection.
the SWFLoader element loads the as2 flash file.
whenever i click the button i do see the trace message but the function test does not get executed on the flex application. any ideas?
update
both applications sit on the same domain, on the localhost actually so no need for allowDomain usage and both applications are web based.
Documentation says AS2 and AS3 LocalConnections should communicate no problems.
Do you need to look into the allowDomain method? Do you need to put a crossdomain.xml file in place? If you do have swfs on two different domains, pay special attention to the send method documentation, because you you have to add additional info to the send method's connection name.
Are they both browser based applications? I not, look into AIR
I created the LocalConnection variable within the init() scope, so when the function ended the localconnection was destroyed. the solution is just to declare the variable outside of the init function.
public var a:LocalConnection;
private function init():void {
a = new LocalConnection();
a.client=this;
a.connect("abcde");
}
Please enlighten this flex noob. I have a remoteobject within my main.mxml. I can call a function on the service from an init() function on my main.mxml, and my java debugger triggers a breakpoint. When I move the remoteobject declaration and function call into a custom component (that is declared within main.mxml), the remote function on java-side no longer gets called, no breakpoints triggered, no errors, silence.
How could this be? No spelling errors, or anything like that. What can I do to figure it out?
mxml code:
< mx:RemoteObject id="myService"
destination="remoteService"
endpoint="${Application.application.home}/messagebroker/amf" >
< /mx:RemoteObject >
function call is just 'myService.getlist();'
when I move it to a custom component, I import mx.core.Application; so the compiler doesn't yell
my child component: child.mxml
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()" >
<mx:Script>
<![CDATA[
import mx.core.Application;
public function init():void {
helloWorld.sayHello();
}
]]>
</mx:Script>
<mx:RemoteObject id="helloWorld" destination="helloService" endpoint="$(Application.application.home}/messagebroker/amf" />
<mx:Label text="{helloWorld.sayHello.lastResult}" />
</mx:Panel>
my main.mxml:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()" xmlns:test="main.flex.*" >
<mx:Script>
<![CDATA[
[Bindable]
public var home:String;
[Bindable]
public var uName:String;
public function init():void {
//passed in by wrapper html
home = Application.application.parameters.appHome;
uName = Application.application.parameters.uName;
}
]]>
</mx:Script>
<test:child />
</mx:Application>
The child components are calling creationComplete before the parent (so home is null). A solution is to throw an event (like InitDataCompleted) from the parent after you read the data, and in the child components listen for this event (so don't rely on creationcomplete in the child).
However more important than that is how can you diagnose in future this kind of problems. A simple tool like a proxy (eg Charles) should help.
For your endpoint value you've got
endpoint="$(Application.application.home}/messagebroker/amf"
Why are you using $( before Application.application... This should be a { as in:
endpoint="{Application.application.home}/messagebroker/amf"
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.