Problem with an Event in a Custom Component - apache-flex

I'm just getting started with custom events in a custom component. And I don't quite have the hang of it, yet.
I've got a component with a button in it. When it's clicked, I want to call a function in the main app.
Custom Component:
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute" >
<mx:Metadata>
[Event(name="goClick", type="mx.events.Event")]
</mx:Metadata>
<mx:Script>
<![CDATA[
private function onButtonClickHandler(event:MouseEvent):void {
dispatchEvent(new Event("goClick"));
}
]]>
</mx:Script>
<mx:Button id="myGoButton" label="Go"
click="onButtonClickHandler(MouseEvent)" />
</mx:Panel>
Main App:
<myFolder:MyComponent
goClick="MyCoolFunction()">
Unfortunately, I'm doing something wrong. It says that the Event type is unavailable. What do I need to change or add? My guess is that I need to declare goClick in some way?
Thank you!
-Laxmidi

No such class as mx.events.Event unless you created one. Change that to flash.events.Event and you'll be good to go.
If you did create an 'mx.events.Event" class, make sure that your dispatch event is create an instance of your custom class and not the flash.events.Event.

Related

Get variable from ItemRenderer to Main Application

I have a List with TextInput as item renderer. I want to get the value entered in the TextInput (form the TextInputItemRenderer) and pass it the main application to do some checks(upon tapping enter on the textInput -- see code below).
I know that we can do it thru dispatching event but I still don't understand how to pass a variable from the ItemRenderer to the main app.
Help Pls.
Thanks
<?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"
autoDrawBackground="true" xmlns:components="components.*" width="100%"
>
<s:layout>
<s:HorizontalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
override public function set data( value:Object ) : void {
super.data = value;
}
protected function myTextInput_enterHandler(event:FlexEvent):void
{
trace(myTextInput.text);
What Next??
}
]]>
</fx:Script>
<components:ClearableTextInput text="{data.label}" id="myTextInput" enter="myTextInput_enterHandler(event)"/>
</s:ItemRenderer>
i'm not sure if I got your question correctly but would this help?
http://www.ajibanda.blogspot.com/2011/02/changing-currentstate-of-main-and.html
Instead of trying to access from MainApp to itemRenderer, i think you can do backward. Follow one of two solutions below:
In itemRenderer, assign value you want to check later to a public global variable on the MainApp. The limitation is you then only enable to check it on MainApp, not any where esle (other itemRenderer, component, module etc.)
Use EvenBus to put the value to a global container. Create a static eventBus instance in AppUtils, for example. In itemRenderer, AppUtils.eventBus.dispatch() an event with the value attached to it each time the value changed. And then use AppUtils.eventBus again to addEventListener() to retrieve the value and check wherever you want. Google AS3Commons for EventBus.

How to check the state of a Radio Button in Flex 3?

I'm working on a template for dynamic questionnaires in Flex. More detailed description in my previous thread HERE
To create the questionnaire, I use nested repeaters - one for the questions and one for the answers (as their amount may vary).
<mx:Repeater id="r" dataProvider="{questions}">
<mx:Label text="{r.currentItem.question}" width="200"/>
<mx:Repeater id="r2" dataProvider="{r.currentItem.answers}">
<mx:RadioButton label="{r2.currentItem.text}" width="200"
click="answerHandler(event, event.currentTarget.getRepeaterItem())"/>
</mx:Repeater>
</mx:Repeater>
To understand my data providers, it's probably best to check the answer for my previous thread - easier than if I try to explain it here.
The question here is... As you can see, I created click event handler for each radio button and my plan was to do playerScore++ every time the user chose correctly (which can be achieved by checking the Boolean property "correct" of sent RepeaterItem).
However, I see now that even if the button is selected already, I can still click on it more times, and even though it's not changing anything in the view, it increments the score every time.. I would also have to handle situation in which the user changes his mind (I could give + points for each good answer and - points for wrong, but this would mean, that if the user chose more wrong answers, my score will be negative and I don't want it).
So it would be way way easier to just have a Submit button and check the states of all my buttons and if they are correct only after the user clicks "submit". Is it possible?
I recommend you to refer to the following sample and add RadioButtonGroup to your repeater groups and listen to change events instead of click. You can listen change event right in RadioButtonGroup and check if (radioGroup.selectedValue.correct) for correctness of new selection. See corresponding documentation.
To have possibility to assign radiogroups unique name you have to extract inner repeater with answers into separate component. Breaking your application into smaller components can make your application more clear and readable. You can treat every MXML file as a class (as in OOP) but in declarative form. And to tell the true every MXML component is a class which inherited from root-node-class.
Lets look at the code.
First, our inner component which serves answers:
<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Metadata>
[Event(name="rightAnswerEvent", type="AnswerEvent")]
</mx:Metadata>
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
public var answers:ArrayCollection;
protected function answerGroup_changeHandler(event:Event):void
{
if (answerGroup.selectedValue.correct)
dispatchEvent(new AnswerEvent(AnswerEvent.RIGHT_ANSWER_EVENT));
}
]]>
</mx:Script>
<mx:RadioButtonGroup change="answerGroup_changeHandler(event)" id="answerGroup" />
<mx:Repeater dataProvider="{answers}" id="answersRepeater">
<mx:RadioButton group="{answerGroup}" label="{answersRepeater.currentItem.text}"
value="{answersRepeater.currentItem}" />
</mx:Repeater>
</mx:VBox>
It gets answers collection as input and fires our custom event to inform some components about right answer (see Metadata tag).
Our custom event is pretty simple an looks like the following:
package
{
import flash.events.Event;
public class AnswerEvent extends Event
{
public static const RIGHT_ANSWER_EVENT:String = "rightAnswerEvent";
public function AnswerEvent(type:String)
{
super(type);
}
}
}
So now our top level component:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application layout="vertical" xmlns:local="*"
xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
private var questions:ArrayCollection = new ArrayCollection();
[Bindable]
private var scoreCounter:int;
]]>
</mx:Script>
<mx:Label text="{'Score ' + scoreCounter}" />
<mx:Repeater dataProvider="{questions}" id="questionRepeater">
<mx:Label text="{questionRepeater.currentItem.question}" />
<local:AnswerGroup answers="{questionRepeater.currentItem.answers}" rightAnswerEvent="scoreCounter++" />
</mx:Repeater>
</mx:Application>
I omitted initialization code to populate our Question and Answer domain objects with data from XML (see previous thread).
So now we have compact modularized code where every part solves its own task.
Hope this helps!

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 custom events bubbling

Dear Richard Szalay,
i go through your answers regarding bubbling, i want explore bubbling more.
Please see my sample below
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:view="com.view.*" >
<mx:Script>
<![CDATA[
import com.events.ImgLoaded;
private function loadedd(evt:ImgLoaded):void{
trace("test")
evt.stopImmediatePropagation();
}
private function panelClickHandler(evt:Event):void{
trace("panel");
}
]]>
</mx:Script>
<mx:VBox>
<mx:Panel click="panelClickHandler(event)">
<view:Load imgLoad="loadedd(event)"/>
</mx:Panel>
</mx:VBox>
</mx:Application>
In my custom event class i set bubbling=true, cancelable=true
I can understand from previous answer that bubbling only affects UI components; events fired from custom classes will not bubble, even if the bubbles argument is set to true.
My question is how can i prevent panelClickHandler function got fired when i click button in the "Load" (custom component)??
please explain bubbling with good example ( like to have with custom event classes)?
I assume your first language isn't english and at any rate I am not sure I fully understand you, but what I think you are asking for is how to allow for a click in the view:load from firing the click handler on the panel.
What you need is to set up an event listener for a click on the view:load component, and stopPropagation from there. That will prevent the click handler on the panel from firing. Example:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:view="com.view.*" >
<mx:Script>
<![CDATA[
import com.events.ImgLoaded;
private function loadedd(evt:ImgLoaded):void{
trace("test")
evt.stopImmediatePropagation();
}
private function panelClickHandler(evt:Event):void{
trace("panel");
}
private function load_clickHandler ( e:MouseEvent ) : void
{
e.stopPropagation;
}
]]>
</mx:Script>
<mx:VBox>
<mx:Panel click="panelClickHandler(event)">
<view:Load imgLoad="loadedd(event)" click="load_clickHandler(event)"/>
</mx:Panel>
</mx:VBox>
</mx:Application>

Why wouldn't a flex remoteobject be able to work within a custom component?

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:
&#060 mx:RemoteObject id="myService"
destination="remoteService"
endpoint="${Application.application.home}/messagebroker/amf" &#062
&#060 /mx:RemoteObject &#062
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"

Resources