I'm new to flex, and using a small open-source UI library ReCoral. In my test, I use its Application class as the root of mxml, and which has a click handler.
<?xml version="1.0" encoding="utf-8"?>
<common:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:common="geb.common.*" width="500" height="500"
xmlns:controls="geb.controls.*"
xmlns:mx="library://ns.adobe.com/flex/mx"
click="hello()">
<fx:Script>
<![CDATA[
private function hello(): void {
trace("world");
}
]]>
</fx:Script>
<controls:Label text="Click Me" height="100" width="100" click="hello()" />
</common:Application>
You can see the code is quite simple.
But when I click the Label, or the Application, the hello() method seems never invoked. I tried to add a breakpoint on the trace("world") line in debug mode, it had never been triggered.
Since the author is quite busy, and I'm new to flex, I don't know how to debug this problem.
Is there any way to listen any click event, and log the information?
Is there any useful document I should read?
Try this. You need to add the event type
<?xml version="1.0" encoding="utf-8"?>
<common:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:common="geb.common.*" width="500" height="500"
xmlns:controls="geb.controls.*"
xmlns:mx="library://ns.adobe.com/flex/mx"
click="hello()">
<fx:Script>
<![CDATA[
private function hello(event:MouseEvent): void {
trace("world");
}
]]>
</fx:Script>
<controls:Label text="Click Me" height="100" width="100" click="hello(event)" />
</common:Application>
I found the reason, that Label in RedCoral set mouseEnabled to false in inited method:
override protected function init():void
{
super.init();
mouseEnabled = false;
mouseChildren = false;
}
See: https://github.com/xiaotie/RedCoral/blob/master/src/geb/controls/Label.as#L112
So we should enable it:
<controls:Label text="Click Me" height="100" width="100" click="hello()" mouseEnabled="true" />
Related
I am new to Flex and am having problems figuring out why this eventlistener always returns null for the event.data. I am trying to implement a simple yes/no prompt for data removal. I have included the skinnable container code and the calling mxml. The alertDB_close handler fires I know that but the event.data is empty. thanks for any suggestions
Main mxml:
protected function button1_clickHandler(event:MouseEvent):void
{
// Create an instance of MyAlertPopUp.
var alertDB:AlertMsgPurge = new AlertMsgPurge();
// Add an event handler for the close event to check for
// any returned data.
alertDB.addEventListener('close', alertDB_closeHandler);
alertDB.open(this, true);
}
private function alertDB_closeHandler(eventP:PopUpEvent):void {
// If commit is false, do data is returned.
rd.text = eventP.data as String;
//return;
/
}
AlertMsgPurge:
<?xml version="1.0" encoding="utf-8"?>
<s:SkinnablePopUpContainer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Script>
<![CDATA[
// //
public function onClick(commit:Boolean):void {
close(true);
}
]]>
</fx:Script>
<s:TitleWindow title="" x="75" y="300">
<s:VGroup horizontalAlign="center" paddingTop="8" paddingBottom="8" paddingLeft="8" paddingRight="8" gap="5" width="100%">
<s:Label text="Warning!! all data will be deleted"/>
<s:Button label="Yes" click="close(true);"/>
<s:Button label="No" click="close(false);"/>
</s:VGroup>
</s:TitleWindow>
With that function modified:
private function alertDB_closeHandler(eventP:PopUpEvent):void {
trace("eventdata:"+eventP.commit);
}
You can see if the user clicked "yes" or "no" then you can do your processing.
Is it what your looking for?
I've just started working on a photo viewer type desktop AIR app with Flex. From the main window I can launch sub-windows, but in these sub-windows I can't seem to access the data I collected in the main window.
How can I access this data?
Or, how can I send this data to the sub-window on creation? It doesn't need to be dynamically linked.
myMain.mxml
<?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"
width="260" height="200"
title="myMain">
<fx:Declarations>
</fx:Declarations>
<fx:Script>
<![CDATA[
public function openWin():void {
new myWindow().open();
}
public var myData:Array = new Array('The Eiffel Tower','Paris','John Doe');
]]>
</fx:Script>
<s:Button x="10" y="10" width="240" label="open a sub-window" click="openWin();"/>
</s:WindowedApplication>
myWindow.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Window name="myWindow"
title="myWindow"
xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
width="640" height="360">
<mx:Script>
<![CDATA[
]]>
</mx:Script>
<mx:Label id="comment" x="10" y="10" text=""/>
<mx:Label id="location" x="10" y="30" text=""/>
<mx:Label id="author" x="10" y="50" text=""/>
</mx:Window>
I realize this might be a very easy question but I have searched the web, read and watched tutorials on random AIR subjects for a few days and couldn't find it. The risk of looking like a fool is worth it now, I want to get on with my first app!
You could add an attribute to your window class, and pass the data from the application.
With an attribute and a setter function :
myWindow.mxml :
<![CDATA[
private var _data : Array;
public function set data(data : Array) : void {
this._data = data;
}
]]>
main
<![CDATA[
public function openWin():void {
var w : myWindow = new myWindow();
w.data = myData;
w.open();
}
public var myData:Array = new Array('The Eiffel Tower',
'Paris','John Doe');
]]>
You could also do it by adding a constructor parameter to your window, but you will have to write your Window component in ActionScript.
(Also : you might want to use MyWindow for the name of your component instead of myWindow, but that's just conventionnal nitpicking).
Also, note that there is a singleton variable Application.application that is accessible to all classes in an Application ; however I don't know if this applies to a WindowedApplication, and either way it is not the recommended approach.
I can't seem to get one of the most basic uses of events working in Flex 4. I've followed multiple tutorials and looked everywhere. From what I can tell, I'm doing everything right, so there must be some stupid mistake somewhere.
I have a main application file that contains a button. On clicking the button, it fires a custom event which I want my listener in my child component to catch. The event IS firing. The child component event listener is NOT catching that event. No clue why. The custom event is copy-pasted from Adobe's tutorial (minus the custom namespace and comments).
Main Application
<?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"
applicationComplete="init()" xmlns:local="*">
<fx:Metadata>
[Event(name="enableChanged", type="EnableChangeEvent")]
</fx:Metadata>
<fx:Script>
<![CDATA[
import EnableChangeEvent;
import TestComponent;
private function doDispatchEvent(event:MouseEvent):void {
if(dispatchEvent(new EnableChangeEvent(EnableChangeEvent.ENABLE_CHANGED, true)))
{
statusLabel.text = "Event was dispatched";
}
}
public function init():void {
myButton.addEventListener(MouseEvent.CLICK, doDispatchEvent);
}
]]>
</fx:Script>
<s:Button x="95" y="83" label="Button" id="myButton" />
<s:Label x="230" y="83" text="" id="statusLabel" />
<local:TestComponent x="95" y="150" width="300" height="400" />
</s:WindowedApplication>
Child Component (Test)
<?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"
width="100%" height="100%"
initialize="init();">
<fx:Script>
<![CDATA[
import EnableChangeEvent;
private function doFinalAction(event:EnableChangeEvent):void {
myLabel.text = "Custom Event Recieved";
}
private function init():void {
addEventListener(EnableChangeEvent.ENABLE_CHANGED, doFinalAction);
}
]]>
</fx:Script>
<s:Label width="300" height="50" text="Should change on click" id="myLabel" />
</s:Group>
Custom Event (from Adobe)
package {
import flash.events.Event;
public class EnableChangeEvent extends Event
{
public function EnableChangeEvent(type:String, isEnabled:Boolean=false) {
super(type);
this.isEnabled = isEnabled;
}
public static const ENABLE_CHANGED:String = "enableChanged";
public var isEnabled:Boolean;
override public function clone():Event {
return new EnableChangeEvent(type, isEnabled);
}
}
}
Timofei Davydik's answer it correct. Since the Application object dispatches the event, if you want your TestComponent object to catch it, you have to add a listener to a reference of the Application object from within your TestComponent. You can use the TestComponent object's inherited property "parentApplication" to get a reference to the Application object`.
Change the following code in TestComponent.mxml:
private function init():void
{
this.parentApplication.addEventListener(EnableChangeEvent.ENABLE_CHANGED, doFinalAction);
}// end function
[UPDATE]
Personally I feel like your going about using events the wrong way, so I made a similar flex application to demonstrate how to use events in flex.
Main.mxml
<?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"
xmlns:components="components.*"
xmlns:events="events.*"
applicationComplete="init()">
<fx:Script>
<![CDATA[
import events.EnableChangeEvent;
public function init():void
{
label1.text = "Application Complete!";
}// end function
private function onTestButtonEnableChanged(e:EnableChangeEvent):void
{
label2.text = "Enabled = " + e.isEnabled;
}// end function
]]>
</fx:Script>
<s:layout>
<s:VerticalLayout horizontalAlign="center" paddingTop="50" />
</s:layout>
<components:TestButton id="testButton" label="CLICK!" enableChanged="onTestButtonEnableChanged(event)" />
<s:Label id="label1"/>
<s:Label id="label2"/>
</s:WindowedApplication>
TestButton.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:Button xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
click="onClick()">
<fx:Metadata>
[Event(name="enableChanged", type="events.EnableChangeEvent")]
</fx:Metadata>
<fx:Script>
<![CDATA[
import events.EnableChangeEvent
private var isEnabled:Boolean;
private function onClick():void
{
isEnabled = !isEnabled;
dispatchEvent(new EnableChangeEvent(EnableChangeEvent.ENABLE_CHANGED, isEnabled));
}// end function
]]>
</fx:Script>
</s:Button>
The main difference between the applications is that the Button object, in this case the TestButton object, dispatches the EnableChangeEvent event when clicked. While dispatching the event we can parse the boolean value for the _isEnabled property of TestButton to the event. Before, the value is inverted using the line isEnabled = !isEnabled. Now we can make use of handling the event using the mxml declaration of the TestButton object with the xml attribute enableChanged.
Also instead of following tutorials you might want to try a book like the one i'm reading. It's called "Adobe Flash Builder 4 and Flex 4 Bible" by David Gassner which you can get as a paperback book or a book for your kindle.
#Taurayi is correct, but to get you current code working with just one change, edit the init of Test Component to look like the following:
private function init():void
{
FlexGlobals.topLevelApplication.addEventListener(EnableChangeEvent.ENABLE_CHANGED, doFinalAction);
}
Your application object dispatches the custom event. Why do you expect your group to "catch" this event? It won't. Custom events have neither capturing nor bubbling phase. So, only listeners added to your application object will listen to this event.
I have a main mxml file (flex4) and want to pass a parameter (user_name) to a component in a directory called components.
When I run the program, the user_name is NOT being sent from the main to the component file.
(Interestingly, if you make the component visible, you can see the parameter has been passed)
New to flex/actionscript and this parameter passing is (without help) quite a pain to progress.
So, help would be very much appreciated.
TIA.
I have hacked much larger files down to get the following two files:
MAIN
<?xml version="1.0" encoding="utf-8"?>
<s:Application
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:components="components.*">
<mx:Button id="editAccount" label="Edit Account" fontSize="16" color="#000000" x="100" y="125" click="AccountForm(event)" />
<components:editAccountForm visible="false" user_name = "username from main" />
<fx:Script>
<![CDATA[
import components.editAccountForm;
import mx.managers.PopUpManager;
private function AccountForm(e:MouseEvent):void
{
var win3:editAccountForm = new editAccountForm();
PopUpManager.addPopUp(win3,this,true);
PopUpManager.centerPopUp(win3);
}
]]>
</fx:Script>
</s:Application>
COMPONENT FILE
<?xml version="1.0" encoding="utf-8"?>
<mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml"
layout="vertical" title="Edit Account Details" x="50" y="600" >
<mx:Form width="100%" height="100%">
<mx:FormItem label="">
<mx:Label width="300" textAlign="center" text="{user_name}"/>
</mx:FormItem>
<mx:FormItem label="Enter your new Email Address">
<mx:TextInput id="email_address2" width="300" maxChars="128" contentBackgroundColor="#F5DC0C"/>
</mx:FormItem>
</mx:Form>
<mx:HBox width="100%" horizontalAlign="center">
<mx:Button id="close" label="Close" click="PopUpManager.removePopUp(this)" />
</mx:HBox>
<mx:Script>
<![CDATA[
[Bindable]
public var user_name:String = "username from Component";
]]>
</mx:Script>
<mx:Script>
<![CDATA[
import mx.core.IFlexDisplayObject;
import mx.events.CloseEvent;
import mx.managers.PopUpManager;
private function closeWindow(e:CloseEvent):void
{
PopUpManager.removePopUp(e.target as IFlexDisplayObject);
}
]]>
</mx:Script>
</mx:TitleWindow>
If you simply want to get the user_name from the main app into your TitleWindow component, just set win3.user_name = user_name after you instantiate win3. If you are looking to bind it to your newly instantiated win3 (which you would do if user_name were expected to change), then you need to look into the BindUtils helper class.
The typical way of getting data back and forth between an app and a dialog is to set the value after you instantiate your dialog, and then add a listener to your dialog so that your app will get notified if something changed. If you are listening for the Close event, for example, you can get the value from the event like so: (event.currentTarget as EditAccountForm).user_name in your app's event handler.
Another common method is to have your window dispatch a custom event (that your main app added a listener to the dialog for) which contains the new value for user_name.
Hope that helps.
I have a Renderer:
<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Metadata>
[Event(name="addToCart",type="event.ProductEvent")]
</fx:Metadata>
<fx:Script>
<![CDATA[
import events.ProductEvent;
import mx.collections.ArrayCollection;
protected function button1_clickHandler(event:MouseEvent):void
{
var eventObj:ProductEvent=new ProductEvent("addToCart",data.price,data.descript);
dispatchEvent(eventObj);
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:states>
<s:State name="normal"/>
<s:State name="hovered"/>
</s:states>
<s:BorderContainer >
<s:layout>
<s:VerticalLayout paddingBottom="10" paddingLeft="10" paddingRight="10" paddingTop="10"/>
</s:layout>
<s:Label text="{data.descript}"/>
<mx:Image source="{data.url}" width="50" height="50" width.hovered="100" height.hovered="100"/>
<s:Label text="{data.price}"/>
<s:Button includeIn="hovered" click="button1_clickHandler(event)" label="buy"/>
</s:BorderContainer>
</s:ItemRenderer>
and the custom event class:
package events
{
import flash.events.Event;
[Bindable]
public class ProductEvent extends Event
{
public var price:String;
public var descript:String;
public function ProductEvent(type:String,price:String, descript:String)
{
super(type);
this.price=price;
this.descript=descript;
}
override public function clone():Event
{
return new ProductEvent(type,price,descript);
}
}
}
but i cannot call that event in a container from the main application
<s:SkinnableDataContainer id="Sk" x="200" y="300" left="100" right="900" dataProvider="{imagesCollection}" itemRenderer="components.ImageRenderer" includeIn="normal" >
<s:layout>
<s:TileLayout/>
</s:layout>
</s:SkinnableDataContainer>
any ideas?
thanks
I want to make this:
<s:SkinnableDataContainer id="Sk" x="200" y="300" left="100" right="900" dataProvider="{imagesCollection}" itemRenderer="components.ImageRenderer" includeIn="normal" ***addToCart=something(event)*** >
Events are not called, so I'm not entirely sure what you want.
You can create an instance of an event class, like this:
var myProductEvent : ProductEvent = new ProductEvent("productEventTypeA", true, ...); // true is for enabling Bubbles, so that the event bubbles up to the listener.
You can dispatch that event from an itemRenderer the same way you would do so if you were using an event elsewhere:
dispatchEvent(myEvent);
Also on the item renderer, declare the event that is going to be dispatched:
[Event(name="productEventTypeA", type="local.events.ProductEvent")]
You can add an event listener on the List or DataGroup component implementing your item renderer so it runs your code after the event is dispatched:
myList.addEventListener("productEventTypeA", onProductEvent); // or a constant instead of "productEventTypeA"
or
myDataGroup.addEventListener("productEventTypeA", onProductEvent); // or a constant instead of "productEventTypeA"
And finally declare your listener function in the same file you added the event listener:
public function onProductEvent(e:ProductEvent):void
{
// do stuff
}
Note: In an itemRenderer, you often want to make your event bubble so that it can be listened to on the component which uses renderers--usually a list based class.
I hope this helps someone, you don't need to do all that...
It's very simple:
ItemRenderer:
protected function dispatchMyCustomEvent():void {
(owner as List).dispatchEvent(new CustomEvent);
}
if the parent isn't a list then just typecast it to what the parent is.
then in your component that has the list you can do the following:
protected function creationComplete():void {
myList.addEventListener(CustomEvent.TYPE, onMyCustomeEvent);
}
protected function onMyCustomEvent(event:MyCustomEvent):void {
// handle the event
}
It's hard to make out what you're trying to do from the question, because of the formatting, and it appears there's some text missing.
However. try making the event bubble, and add the event listener on the list which holds the itemRenderers.
Eg:
<mx:Canvas creationComplete="list1.addEventListener('addToCart',onAddToCart)">
<mx:List id="list1" itemRenderer="com.foo.YourItemRenderer" />
</mx:Canvas>
<!-- YourItemRenderer.mxml -->
<mx:Canvas>
<!-- Other item renderer stuff -->
<mx:Button click="dispatchEvent(new ProductEvent('addToCart'))" />
</mx:Canvas>
// ProductEvent.as
public class ProductEvent {
public function ProductEvent(type:String,bubbles:Boolean=true) {
super(type,bubbles);
// ... etc
}
}
Perhaps this is a little out of scope, but that sort of signaling with events is where MVC frameworks like Cairngorm shine. I usually think of them as AS event buses.
This is possible using the Event metadata tag: http://livedocs.adobe.com/flex/3/html/help.html?content=createevents_3.html
You'll have to sub-class SkinnableDataContainer and add the metadata tag:
[Event(name="addToCart", type="events.ProductEvent")]
public class MySkinnableDataContainer extends spark.components.SkinnableDataContainer
{
}
You can also do it in MXML:
<?xml version="1.0"?>
<!-- ../MySkinnableDataContainer.mxml -->
<s:SkinnableDataContainer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark">
<mx:Script>
<![CDATA[
import events.ProductEvent;
]]>
</mx:Script>
<mx:Metadata>
[Event(name="addToCart", type="events.ProductEvent")]
</mx:Metadata>
</s:SkinnableDataContainer>
Then use this new class instead of the SkinnableDataContainer:
<MySkinnableDataContainer id="Sk" x="200" y="300" left="100" right="900" dataProvider="{imagesCollection}" itemRenderer="components.ImageRenderer" includeIn="normal" addToCart="something(event)" />
Note: depending on your events bubble property you may have to catch it and then re-forward it along inside of MySkinnableDataContainer.
Make the event bubble:
public function ProductEvent(type:String,price:String, descript:String)
{
super(type, true); // Add bubbles = true instead of default false
this.price=price;
this.descript=descript;
}
Then in your app:
<s:SkinnableDataContainer id="Sk" x="200" y="300" left="100" right="900" dataProvider="{imagesCollection}" itemRenderer="components.ImageRenderer" includeIn="normal" creationComplete="Sk.addEventListener('addToCart', myFunctionToHandle)">
Add an event listener on creation complete.