Behold this example:
addEventListener("myEventType", myFunction("argument"));
function myFunction(args:String):Function {
return function(evt:Event):void {
trace(evt.currentTarget, "has", args);
};
}
dispatchEvent(new Event("myEventType", true));
It works.
Can I do something similar, but passing "argument" through dispatchEvent()?
It'd be very handy in a situation where dispatchEvent() is in a wholly separated class from addEventListener() and myFunction().
I'll be needing this a lot, so I want to do it without creating a custom event class for every situation.
You can use native flash.events.DataEvent for passing String parameter or create custom DataEvent with data:* property in all situations where you need to pass parameters to event handler.
If you want to customize the behavior of event listener in the place of adding event listener you can create "listener" object for holding this custom parameters (but I think this technique is more complicated than custom events):
addEventListener("myEventType", new EventListener("param1").onEvent);, whereEventListener is the class like this:
public class EventListener
{
private var params:*;
public function EventListener(params:*)
{
this.params = params;
}
public function onEvent(event:Event):void
{
trace("onEvent, params = ", params);
}
}
You could take a look at Signals (https://github.com/robertpenner/as3-signals). They are an alternative to Events and you can send whatever extra params you want with a Signal.
Related
I often have the requirement to dispatch a flash.events.Event with soem custom String text, like:
protected function mouseClicked(event:Event) {
//here I'd want to notify anyone interested in the button click,
//and also transfer the name of the button (or whatever) that was clicked - assume some dynamic value
dispatchEvent(new Event("myMouseEvent"), button.name));
}
Of course the above event is invalid. But is there any event that can be used for that type of events? Maybe the TextEvent, but I don't know if I'd be misusing it here.
To include additional data with your events, create a custom event class by extending Event (or any sub-class of Event) and adding your own properties. For example:
class NameEvent extends Event {
public static const NAME_CLICK:String = "nameClick";
public var name:String;
public function NameEvent(type:String, name:String) {
this.name = name;
super(type);
}
}
dispatchEvent(new NameEvent(NameEvent.NAME_CLICK, button.name));
Note that your event type strings ("nameClick" in this example) should be globally unique, otherwise listeners could get them confused with other event types. For example "click" is already expected to be a MouseEvent. I often add prefixes to my custom event types, for example "NameEvent::click".
Another option that does not require creating a custom event is to rely on the expected target to get additional data. For example:
// dispatch a custom event from a Button
dispatchEvent(new Event("myClick"));
// handler for "myClick" events on the button
function myClicked(e:Event):void {
var button:Button = e.target as Button;
trace(button.name);
}
This is not as flexible and also more fragile than using a custom event class, but sometimes a quick easy solution.
Basically I want to check if a user exists in a database using AMF (and that works great!). But then I want to return the boolean value to another function (in another class) that originally called the "checkUserExistance" function. But, since the database connection isn't immidiate, this function will always return a false value (even if "result" is true). So I would like to have the return-line inside the "onUserChecked"-function but that of course gives me an error. I thought I could create an eventListener, but then, the "return userExists"-line would also have to be inside another function, which doesnät work(?)... What can I do?
public function checkUserExistance(username:String) {
var responderBOOLEAN:Responder = new Responder(onUserChecked, onFault)
var userExists:Boolean = false;
connection.connect(gateway);
connection.call("User.checkUser", responderBOOLEAN, username);
connection.close();
function onUserChecked(result:Boolean):void {
userExists = result;
}
return userExists;
}
I'm sorry but you are trying to force an Asynchronous call to a Synchronous one and this is WRONG.
See here
You should learn how to handle events in the correct way.
What can i suggest you that helped me a lot is this
The only true answer here is to save userExists as a member variable, and dispatch event when the server returns you a response. The client side of the things should be similar to:
// add listener, ServerEvent is a custom event (see below)
server.addEventListener(ServerEvent.CHECK_RESPONSE, onCheckResponse);
server.checkUserExistance('username'); // start the query
function onCheckResponse(e:ServerEvent):void {
if (e.userExists) {
}
}
// inside server class
function onUserChecked(result:Boolean):void {
userExists = true;
dispatchEvent(new ServerEvent(ServerEvent.CHECK_RESPONSE, userExists));
}
/* ServerEvent is a custom class that extens Event
Such classes are used so you can pass special properties in them
via constructor (pass data, store it into member variable)
and through getter for that variable.
If you don't like it, simply add/dispatch Event.COMPLETE
and use public property to get userExists from server
*/
This is my code in Flash/AS3, in main class.
addEventListener(Event.ENTER_FRAME,function(e:Event){
if(findObject == true){
// I want to remove this ENTER FRAME
}
});
try this:
e.currentTarget.removeEventListener(e.type, arguments.callee)
You shouldn't be doing what you do in the code above.
The mgraph's code has a tiny chance of failing to work as advertised if the currentTarget of the event doesn't have a removeEventListener() method (possible, but very unlikely). From the compiler standpoint though you will be trying to dynamically resolve the method on a generic object which is error prone and should be handled with care. This is hazardous because it shows that the programmer "did not know" what kind of object was she expecting to handle and worked by assumption. Assumptions are great for finding a solution but are equally bad for implementing one.
If you thought of optimizing something in the way you did it, then, just FYI this actually creates a unique (redundant) name in the symbol table (in the compiled SWF file) which causes worse compression of the SWF.
If you are doing this as a matter of experiment, this is fine, but you should avoid such code in real life projects.
One more thing to be aware of: comparison to true constant is 100% useless. If such comparison makes any sense at all (i.e. findObject may evaluate to false any time), then if (findObject) { ... } is equivalent but shorter version of your code.
Last thing, hopefully, the anonymous function is missing return type declaration. It won't really change much in your example, except that you will get compiler warning. Omitting type declaration is, in general, a bad style.
EDIT
public function addEventListener(type:String, listener:Function ...):void
{
this._listeners[type].push(listener);
}
public function dispatchEvent(event:Event):void
{
for each (var listener:Function in this._listeners[event.type])
listener(event);
}
public function removeEventListener(type:String, listener:Function, ...):void
{
delete this._listeners[type][listener];
}
Suppose you actually want to implement IEventDispatcher (instead of using another EventDispatcher - you may have your reasons to do so, one such reason is that native EventDispatcher generates insane amounts of short-lived objects - events, and you may want to reduce that.) But there is no way you can replicate event.target or event.currentTurget in your code because you can't access the object owning the method, so, you would leave that out.
Another example:
public class SomeEvent extends Event
{
private var _target:NotEventDispatcher;
public function SomeEvent(type:String, someTarget:NotEventDispatcher)
{
super(type);
this._target = someTarget;
}
public override function get target():Object
{
return this._target;
}
}
This is something that I actually saw in real world, this was used in either Mate or similar framework to sort of "anonymously" connect all event dispatchers to a single static instance of some "mothership event dispatcher".
I don't necessarily justify this approach, but, technically, nothing stops you from doing either one of these. What I was saying in my post above is that in certain situations the language promises you things, like, if you did:
var dispatcher:IEventDispatcher;
try
{
dispatcher = IEventDispatcher(event.currentTarget);
// now you can be sure this object has removeEventListener
dispatcher.removeEventListener(event.type, arguments.callee);
}
catch (error:Error)
{
// but what are you going to do here?
}
But the most common case would be you subscribing to a bubbling event, in which case, you don't know whether you want to unsubscribe from event.target or event.currentTtarget - because you don't know which one is that you are listening to.
I agree with wvxvw.
Another way to approach your problem is to have a variable to control the "state" of your ENTER_FRAME event:
private var _state:String;
private function init(e:Event):void {
addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
}
private function loop(e:Event):void {
switch(_state) {
case "play":
// do play stuff
// when you want to pause
// goToPause();
break;
}
}
// you can call the method below from a button or whatever you want
private function goToPause():void {
_state = "pause";
// do some stuff here
// when you are done, switch "_state" back to "play"
}
In this example, you keep listening for ENTER_FRAME, but it only does things when the _state variable is set to "play". You can also remove the event listener in the goToPause method:
private function goToPause():void {
_state = "pause";
removeEventListener(Event.ENTER_FRAME, loop);
}
However, the nice thing about using the "_state" to switch things is that you don't end up having a mess of addEventListeners and removeEventListeners (which is what can happen depending on how complicated your loop gets) that you have to keep track of.
You should not use anonymous function call if you would like to remove listener some time later.
public function main():void
{
//...
//some method, where you add event listener
//...
//adding enterFrame event listener
this.addEventListener(Event.ENTER_FRAME,enterFrameHandler);
//...
}
private function enterFrameHandler(e:Event)
{
if(findObject) // " == true" is not really necessary here.
{
// removing enterFrame listener:
this.removeEventlistener(Event.ENTER_FRAME,enterFrameHandler);
}
}
Just for a completeness with the other techniques mentioned here, the function you are creating is a unbound closure, so you can also leverage that concept to reference both your function and dispatcher.
var callback:Function;
var dispacher:IEventDispatcher = this;
addEventListener(Event.ENTER_FRAME, callback = function(e:Event){
if(findObject == true){
dispacher.removeEventListener(Event.ENTER_FRAME, callback);
}
});
Normal closed-over variable rules apply.
I have created a custom event in flex 3.5. But the handler is not invoked. How to solve this or what is the way to debug this problem?
The Event class:
package com.saneef.worldlanguages.events
{
import flash.events.Event;
public class LanguageEvent extends Event
{
public static const LANGUAGE_SELECTED:String = "LanguageSelected";
public function LanguageEvent(type:String,languageid:String)
{
super(type);
this.langid = languageid;
trace("LanguageEvent: " + this.langid);
}
public var langid:String;
override public function clone():Event {
return new LanguageEvent(type, langid);
}
}
}
Dispatching:
private function functionOne():void
{
try{
dispatchEvent(new LanguageEvent(LanguageEvent.LANGUAGE_SELECTED,"STR"));
}
catch(e:Error)
{
trace(e.message);
}
}
In the Main application class, EventListener:
protected function application1_initializeHandler(event:FlexEvent):void
{
this.addEventListener(LanguageEvent.LANGUAGE_SELECTED,
application1_LanguageSelectionHandler);
}
The event handler function:
public function application1_LanguageSelectionHandler(event:LanguageEvent):void
{
trace("application1_LanguageSelectionHandler: " + event.langid);
populate_countrya3id_languages(event.langid);
}
Your code looks fine. Since I can't see the full source, here are my two thoughts on what may be going on:
Are you sure your addEventListener call is done before you dispatch the event? Add some trace to make sure the application1_initializeHandler prints before functionOne does.
Is your functionOne call in another different component than your main application? If so, you'll need to set your custom event's bubbles attribute to true in your event's super call.
public function LanguageEvent(type:String,languageid:String,bubbles:Boolean=True)
{
super(type, bubbles);
this.langid = languageid;
trace("LanguageEvent: " + this.langid);
}
See the flash.events.Event docs for the constructor call. Also, here's a quote about the bubbles argument explained here:
The bubbles property
An event is said to bubble if its
event object participates in the
bubbling phase of the event flow,
which means that the event object is
passed from the target node back
through its ancestors until it reaches
the Stage. The Event.bubbles property
stores a Boolean value that indicates
whether the event object participates
in the bubbling phase. Because all
events that bubble also participate in
the capture and target phases, any
event that bubbles participates in all
three of the event flow phases. If the
value is true, the event object
participates in all three phases. If
the value is false, the event object
does not participate in the bubbling
phase.
Based on your source code, it looks like you've seen the "Dispatching Custom Events" in the flex docs, but I'll link to it anyways for future/easy reference: http://livedocs.adobe.com/flex/3/html/help.html?content=createevents_3.html.
Also, check out http://www.adnandoric.com/2008/12/29/understanding-the-flex-event-propagation/ for a high-level overview of the event propagation system to try to get a better understanding of what's going on while developing.
Edit:
Based on your comments I'm guessing your functionOne call is in a separate class and your "main" application has an instance of this class. If that's so you'll want to attach your event listener on that instance and delegate it to your main's application1_LanguageSelectionHandler function... Like so:
protected function application1_initializeHandler(event:FlexEvent):void
{
this.theInstanceThatHoldsYourFunctionOne.addEventListener(LanguageEvent.LANGUAGE_SELECTED,
application1_LanguageSelectionHandler);
}
I've done a lot of reading through forum posts and tutorials, but I still can't wrap my brain round events and event listeners. I have a pretty simple example, but I can't get it to work.
I have an arrayCollection of custom objects in a repeater, when one of those objects is clicked, I want a different componenet to display data associated with that object.
Here's what I have, but the listener never responds (the dispatcher seems to be working though, because the new event is created and I can see the trace with the proper output.) I suspect it is because when I call addEvent Listener, I am doing so on the wrong object. My understanding is that the object that will display the object data is the object that should have the event listener, and listen for all events of this nature, but maybe I misunderstood.
My custom event:
public class SelectObjectEvent extends Event
{
public function SelectObjectEvent(customEventString:String, myObject:customObject)
{
super(customEventString, true, false);
trace(customEventString+" "+myObject);
}
}
}
My custom object has the following function which is called on click:
public function selectObject(myObject:customObject):void
{
dispatchEvent(new SelectObjectEvent("OBJECT_SELECTED", customObject));
}
And the component I want to display the selected object has the following constructor:
public function SelectedObjectDisplayClass()
{
addEventListener("OBJECT_SELECTED", this.showObject)
}
public function showObject(event:Event):void
{
trace("Show object: "+event);
}
It's not quite clear where your last two code chunks are, but it looks like you need to be calling addEventListener on the object that extends EventDispatcher.
That is, if your second chunk belongs to a custom object called Clickable, which extends EventDispatcher and calls dispatchEvent() when clicked, then your component should be calling myClickable.addEventListener(...) where myClickable is an instance of Clickable. Does that make sense?
But assuming your 3rd code chunk is not in the same class as the second, it doesn't look like you're doing that. You're adding a listener to the class that owns the third chunk of code, which I gather is not the one that extends EventDispatcher.
Just a quick glance at your code and notice that your dispatchEvent second parameter is the class not the instance of the object. Shouldn't this be myObject?
public function selectObject(myObject:customObject):void
{
dispatchEvent(new SelectObjectEvent("OBJECT_SELECTED", **customObject**));
}