Can't removeEventlistener - actionscript-3

I cant remove Event of rute_button, rute_button.removeEventlistener is not work.
is there something wrong about function tampil_rute(t) ? thanks..
function tampil(t)
{
rute_button.addEventListener(MouseEvent.CLICK, tampil_rute(t));
//tampil_rute(t);
var _loadertampil:URLLoader = new URLLoader();
var _datatampil:XML = new XML();
_loadertampil.addEventListener(Event.COMPLETE, readXMLtampil);
_loadertampil.load(new URLRequest("http://localhost/mall_baru/tampil2.php?id="+t));
function readXMLtampil(evttampil:Event)
{
_datatampil = new XML(evttampil.target.data);
var tampilanx = _datatampil..tenant_name;
tampilan.text = String(tampilanx);
trace("tampilan ="+tampilanx);
}
}
function tampil_rute(t)
{
return function( f:MouseEvent )
{
var c = t.split("_", 2);
var d:String = String(c[0]);
var e:Number = Number(c[1]);
for(var i:Number=1; i<=e; i++)
{
tambahan_tampil_rute(d,i);
}
rute_button.removeEventListener(MouseEvent.CLICK, tampil_rute(t));
}
}
function tambahan_tampil_rute(d, i)
{
this["rute_"+d+"_"+i].visible=true;
}

The problem is in the returned function by tampil_rute(t), each time you call this function this is returning a new object of type Function, if you want to remove the event listener, you must be sure to pass the same object (Function) to the removeEventListener function.
You can fix it as follow:
function tampil_rute(t)
{
var listener:*; // create a variable to store your listener reference
listener = function( f:MouseEvent )
{
var c = t.split("_", 2);
var d:String = String(c[0]);
var e:Number = Number(c[1]);
for(var i:Number=1; i<=e; i++)
{
tambahan_tampil_rute(d,i);
}
rute_button.removeEventListener(MouseEvent.CLICK, listener); // Put the reference here
}
return listener; // Return the listener here
}

fenixkim is right, the event handler returned by the anonymous function is not accessible because there is no reference to it.
An alternative solution though is to store t in a variable.
That way, we eliminate the need for an anonymous function and simplify the removal of the event handler.
var t;
function tampil(t)
{
this.t = t;
rute_button.addEventListener(MouseEvent.CLICK, tampil_rute);
// rest of code
}
function tampil_rute(e:MouseEvent)
{
var c = t.split("_", 2);
var d:String = String(c[0]);
var e:Number = Number(c[1]);
for(var i:Number=1; i<=e; i++)
{
tambahan_tampil_rute(d,i);
}
rute_button.removeEventListener(MouseEvent.CLICK, tampil_rute);
}

You don't pass in the parameter for add/remove eventListener.
rute_button.addEventListener(MouseEvent.CLICK, tampil_rute);
rute_button.removeEventListener(MouseEvent.CLICK, tampil_rute);

Related

AS3: Casting to Vector

I am trying to create a vector from unknown class, but it fails, any ideas about how to do it?
This is what i tried:
var vector:Vector = new Vector(); // throw exception
function base():void{
var vector:Vector.<String> = createVector(String);// throw classCastException
}
function createVector(cls:Class):*{
var array:Array = new Array();
for(var i:int = 0; i < 10; i++){
var element:cls = new cls();
array.push(element);
}
return Vector(array);
}
Vector is expecting a parameter type so you can't do this like you want, but using getQualifiedClassName to get class info you can construct a string that will enable you to call the Vector. constructor using getDefinitionByName :
Ex.
// get class parameter name
var className:String=getQualifiedClassName(String);
// get the Vector class object for the given class
var o:Object=getDefinitionByName("__AS3__.vec::Vector<"+className+">");
// call the constructor
var v:*=o.prototype.constructor(["hello", "world"]);
So your function can be written as:
public function createVector(cls:Class):*{
var cn:String = getQualifiedClassName(cls);
var o:Object = getDefinitionByName("__AS3__.vec::Vector.<"+cn+">");
var array:Array = [];
for(var i:int = 0; i < 10; i++){
var element:* = new cls();
array.push(element);
}
return o.prototype.constructor(array);
}
live example at wonderfl:
http://wonderfl.net/c/pkjs
Based on #Patrick answer I found a working solution.
Check it out:
function createVector(cls:Class):*{
var className:String = getQualifiedClassName(cls);
var vectorClass:Class = getDefinitionByName("__AS3__.vec::Vector.<"+className+">") as Class;
var vector:* = new vectorClass(10);
for(var i:int = 0; i < 10; i++){
var element:MyClass = new cls(); // may be Object or Object extends cls
vector[i] = element;
}
return vector;
}

Drag and Drop Game using AS3

import flash.events.MouseEvent;
import flash.utils.Timer;
import flash.events.TimerEvent;
var wordArray:Array = [mc_clipA, mc_clipB];
var imgArray:Array = [Aimg, Bimg];
var posArray:Array = [ {x:816.15, y:396.05}, {x:518.3, y:410.05}];
var ClipA:MovieClip;
var ClipB:MovieClip;
var startXpositionClipA:Number;
var startYpositionClipA:Number;
var startXpositionClipB:Number;
var startYpositionClipB:Number;
wordArray[0].buttonMode = true;
wordArray[1].buttonMode = true;
wordArray[0].addEventListener(MouseEvent.MOUSE_DOWN, item_onMouseDownClipA);
wordArray[1].addEventListener(MouseEvent.MOUSE_DOWN, item_onMouseDownClipB);
//Mouse Down Function for ClipA
function item_onMouseDownClipA(event:MouseEvent):void {
ClipA = MovieClip(event.currentTarget);
startXpositionClipA = ClipA.x;
startYpositionClipA = ClipA.y;
addChild(ClipA);
ClipA.startDrag();
stage.addEventListener(MouseEvent.MOUSE_UP, stage_onMouseUpClipA);
wordArray[0].removeEventListener(MouseEvent.MOUSE_DOWN, item_onMouseDownClipA);
wordArray[1].removeEventListener(MouseEvent.MOUSE_DOWN, item_onMouseDownClipB);
}
//Mouse Up Function for ClipA
function stage_onMouseUpClipA(event:MouseEvent):void {
stage.removeEventListener(MouseEvent.MOUSE_UP, stage_onMouseUpClipA);
ClipA.stopDrag();
var indexClipA:int = wordArray.indexOf(ClipClipA);
var matchClipClipA:MovieClip = MovieClip(imageArray[indexClipA]);
if(matchClipClipA.hitTestPoint(ClipA.x, ClipA.y, true)) {
ClipA.x = positionArray[indexClipA].x;
ClipA.y = positionArray[indexClipA].y;
ClipA.removeEventListener(MouseEvent.MOUSE_DOWN, item_onMouseDownZip);
ClipA.buttonMode = false;
var tot=0;
tot=tot+10;
score.text="You scored: " + tot +" Points ";
var setTimer:Timer = new Timer(1000,60);
setTimer.addEventListener(TimerEvent.TIMER,doTask);
setTimer.start();
function doTask(event:TimerEvent) {
gotoAndStop(1,"Scene 2");
setTimer.stop();
}
}
else
{
ClipA.x = startXpositionClipA;
ClipA.y = startYpositionClipA;
wordArray[0].addEventListener(MouseEvent.MOUSE_DOWN, item_onMouseDownClipA);
wordArray[1].addEventListener(MouseEvent.MOUSE_DOWN, item_onMouseDownClipB);
}
}
//Mouse Down Function for ClipB
function item_onMouseDownClipB(event:MouseEvent):void {
ClipB = MovieClip(event.currentTarget);
startXpositionClipB = ClipB.x;
startYpositionClipB = ClipB.y;
addChild(ClipB);
ClipB.startDrag();
stage.addEventListener(MouseEvent.MOUSE_UP, stage_onMouseUpClipB);
wordArray[0].removeEventListener(MouseEvent.MOUSE_DOWN, item_onMouseDownClipA);
wordArray[1].removeEventListener(MouseEvent.MOUSE_DOWN, item_onMouseDownClipB);
}
//Mouse Up Function for ClipB
function stage_onMouseUpClipB(event:MouseEvent):void {
stage.removeEventListener(MouseEvent.MOUSE_UP, stage_onMouseUpClipB);
ClipB.stopDrag();
var indexClipB:int = wordArray.indexOf(ClipB);
var matchClipClipB:MovieClip = MovieClip(imageArray[indexClipB]);
if(matchClipClipB.hitTestPoint(ClipB.x, ClipB.y, true)) {
ClipB.x = positionArray3[indexTie].x;
ClipB.y = positionArray3[indexTie].y;
ClipB.removeEventListener(MouseEvent.MOUSE_DOWN, item_onMouseDownClipB);
ClipB.buttonMode = false;
var tot=0;
tot=tot;
score.text="You scored: " + tot +" Points ";
var setTimer:Timer = new Timer(1000,60);
setTimer.addEventListener(TimerEvent.TIMER,doTask);
setTimer.start();
function doTask(event:TimerEvent) {
gotoAndStop(1,"Scene 2");
setTimer.stop();
}
}
else
{
ClipB.x = startXpositionClipB;
ClipB.y = startYpositionClipB;
wordArray[0].addEventListener(MouseEvent.MOUSE_DOWN, item_onMouseDownClipA);
wordArray[1].addEventListener(MouseEvent.MOUSE_DOWN, item_onMouseDownClipB);
}
}
This is regarding a drag n drop game. A particular object name will be displayed to the user then the user has to drag n drop the correct image to specified position and once the task is completed it will dynamically navigate to the next scene. For that i have used the timer class. I am getting this error in the scene 2 but in scene 1 the code runs perfectly.
The naming conventions are all correct but i still get the following error,
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at MethodInfo-1156()
at flash.utils::Timer/_timerDispatch()
at flash.utils::Timer/tick()
Can someone help me with this.
Your locally declared method "doTask" is removed right after the execution of the mouse listener. You should either create an anonymous inline listener
var setTimer:Timer = new Timer(1000,60);
setTimer.addEventListener(TimerEvent.TIMER, function(event:TimerEvent):void {
gotoAndStop(1,"Scene 2");
setTimer.stop();
});
setTimer.start();
Some like to do so but I am not a big fan of inline declaration. I would create a real method in my class as a listener.
private var setTimer:Timer;
function stage_onMouseUpClipB(event:MouseEvent):void {
...
setTimer = new Timer(1000,60);
setTimer.addEventListener(TimerEvent.TIMER,doTask);
...
}
function doTask(event:TimerEvent):void {
gotoAndStop(1,"Scene 2");
setTimer.stop();
}

AS3 Closure Confusion

I have a small loop
var a:Array = [{name:Test1},{name:Test2},{name:Test3},{name:Test4}]
var b:GenericButton; //A pretty basic button component
for(var i:int = 0; i < a.length; i++){
b = new GenericButton(a[i].name, function():void { trace(i) });
this.addChild(b);
}
The function supplied to the GenericButton is executed when the button is pressed.
The problem I am having is that when the no matter what button I press the value of 4 (the length of the array) is always output.
How would I ensure that I trace 0 when the first button is pushed, 1 when the second is pushed, etc?
Well, you can simply do:
var f:* = function():void { trace(arguments.callee.index) };
f.index = i;
b = new GenericButton(a[i].name, f);
Better still:
function createDelegate(obj:Object, func:Function):Function
{
var f:* = function ():* {
var thisArg:* = arguments.callee.thisArg;
var func:* = arguments.callee.func;
return func.apply(thisArg, arguments);
};
f.thisArg = obj;
f.func = func;
return f;
}
...
for (...) {
b = new GenericButton(a[i].name,
createDelegate({index: i}, function():void { trace(this.index) }));
}
And in some (most?) cases it would be even better if you created a separate class and passed i into the constructor.
This is most basic error when using closures. You might be thinking that i is set when GenericButton is created. But closure just get direct link to variable i and uses this link when anonymous function is called. By this time, cycle is finished, and all links to i are pointing to same integer with value = 4.
To fix this, just pass value of i somehow - for example, as an additional argument to GenericButton constructor. In this case, a copy of i will be created on each step, with values of 0, 1, 2, 3 - just like you need.
...
b = new GenericButton(a[i].name, function(i:int):void { trace(i); }, i);
...
Store i in GenericButton and pass into function - this causes anonymous function to stop using context variable i (cycle counter) and forces it to use argument i.
Make a function that returns a function. Here's a FlexUnit test method that demonstrates it.
[Test]
public function closureWin():void
{
var functions:Array = [];
var mkFn:Function = function(value:int):Function
{
return function():int
{
return value;
}
}
var i:int;
for (i = 0; i < 10; i++)
{
functions.push(mkFn(i));
}
var j:int;
for(j = 0; j < 10; j++)
{
assertEquals(j, functions[j]());
}
}
Here's a test method demonstrating the behavior you are seeing:
[Test]
public function closureFail():void
{
// basically to see if this works the same way in as3 as it does in javascript
// I expect that all the functions will return 10
var i:int;
var functions:Array = [];
for (i = 0; i < 10; i++)
{
functions.push(function():int{return i});
}
var j:int;
for each (var f:Function in functions)
{
assertEquals(10, f());
}
}

How to pass a variable into an Event.COMPLETE function?

I am running a loop to pull thumbs into a containing movieclip from an xml list. What I want to do is have the thumb's parent movieclip fade in after they are done loading, but I can't figure out how to reference the parent once it's loaded.
My code(which currently doesn't work the way I want it):
var vsThumb:articleBox;
var currentarticleX:Number = 0;
var articleLinkURL:String;
var articleImageURL:String;
var articleText:String;
var vsThumbLoader:Loader;
var next_x:Number;
next_x = 9;
var thumbAlphaTween:Tween;
var articlevsThumb:Array = new Array();
function loadarticleHeadlines():void
{
for (var i:int = 0; i < egarticleXml.articlelist.articleitem.length(); i++)
{
vsThumb = new articleBox();
vsThumb.alpha = 0;
vsThumbLoader = new Loader();
vsThumbLoader.load(new URLRequest(egarticleXml.articlelist.articleitem[i].articlethumbnail));
articleListContainter.addChild(vsThumb);
vsThumb.articleImage.addChild(vsThumbLoader);
vsThumb.articleTitle.text = egarticleXml.articlelist.articleitem[i].articletitle;
titleAutosize(vsThumb.articleTitle);
vsThumb.x = next_x;
next_x += 260;
articlevsThumb[i] = vsThumb;
vsThumbLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, showBox);
vsThumb.clickBtn.buttonMode = true;
}
function showBox(event:Event):void
{
thumbAlphaTween = new Tween(articlevsThumb[i],"alpha",None.easeNone,0,1,.25,true);
}
}
So how do I refer back to the loader's parent so I can fade in the whole movieclip? Can I pass a variable into the showBox function?
Don't use nested functions. They tend to make things more complicated.
i will always have the end value (articleitem.length()-1) in all of the event handlers you create, because its scope is the outer function, loadarticleHeadlines (it will increase by 1 on every iteration). That's probably why your code doesn't work.
The event will be fired on the loaderInfo of your loader, so you can find the loader's parent by using event.target.loader.parent:
function loadarticleHeadlines() : void
{
for (var i:int = 0; i < egarticleXml.articlelist.articleitem.length(); i++)
{
vsThumb = new articleBox();
vsThumb.alpha = 0;
vsThumbLoader = new Loader();
vsThumbLoader.load(new URLRequest(egarticleXml.articlelist.articleitem[i].articlethumbnail));
articleListContainter.addChild(vsThumb);
vsThumb.articleImage.addChild(vsThumbLoader);
vsThumb.articleTitle.text = egarticleXml.articlelist.articleitem[i].articletitle;
titleAutosize(vsThumb.articleTitle);
vsThumb.x = next_x;
next_x += 260;
articlevsThumb[i] = vsThumb;
vsThumbLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, showBox);
vsThumb.clickBtn.buttonMode = true;
}
}
function showBox(event:Event):void
{
thumbAlphaTween = new Tween(event.target.loader.parent,"alpha",None.easeNone,0,1,.25,true);
}
You don't need to pass a variable to your showBox, use the target property of the Event to retrieve the Loader:
function showBox(event:Event):void
{
var li:LoaderInfo=LoaderInfo(event.target);
// be nice remove your listener when your are done
li.removeEventListener(Event.COMPLETE, showBox);
var ldr:Loader=li.loader; // here is your loader
// do whatever you want with loader
thumbAlphaTween = new Tween(articlevsThumb[i],"alpha",None.easeNone,0,1,.25,true);
}

Closure problem? - passing current value of a variable

I'm trying to pass the current value of a variable when an a dynamically generated navigation 'node' is clicked. This needs to just be an integer, but it always results in the last node's value.. have tried some different methods to pass the value, a custom event listener, a setter, but I suspect it's a closure problem.. help would be appreciated ;-)
function callGrid():void {
for (var i:Number = 0; i < my_total; i++) {
var gridnode_url = my_grid[i].#gridnode;
var news_category= my_grid[i].#category;
var newstitle = my_grid[i].#newstitle;
var news_content = my_grid[i]..news_content;
var news_image = my_grid[i]..news_image;
var gridnode_loader = new Loader();
container_mc.addChild(gridnode_loader);
container_mc.mouseChildren = false;
gridnode_loader.load(new URLRequest(gridnode_url));
gridnode_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, gridLoaded);
gridnode_loader.name = i;
text_container_mc = new MovieClip();
text_container_mc.x = 0;
text_container_mc.mouseEnabled = false;
var textY = text_container_mc.y = (my_gridnode_height+18)*y_counter;
addChild(text_container_mc);
var tf:TextSplash=new TextSplash(newstitle,10,0,4 );
container_mc.addChild(tf);
tf.mouseEnabled = false;
tf.height = my_gridnode_height;
text_container_mc.addChild(tf);
var text_container_mc_tween = new Tween(text_container_mc, "alpha", Strong.easeIn, 0,1,0.1, true);
gridnode_loader.x = (my_gridnode_width+5) * x_counter;
gridnode_loader.y = (my_gridnode_height+15) * y_counter;
if (x_counter+1 < columns) {
x_counter++;
} else {
x_counter = 0;
y_counter++;
}
}
}
function gridLoaded(e:Event):void {
var i:uint;
var my_gridnode:Loader = Loader(e.target.loader);
container_mc.addChild(my_gridnode);
_xmlnewstarget = my_gridnode.name;
//||||||||||||||||||||||||||||||||||||||||
//when a particular grid node is clicked I need to send the current _xmlnewstarget value to the LoadNewsContent function...
//||||||||||||| ||||||||||||||||||||||||
my_tweens[Number(my_gridnode.name)]=new Tween(my_gridnode, "alpha", Strong.easeIn, 0,1,0.1, true);
my_gridnode.contentLoaderInfo.removeEventListener(Event.COMPLETE, gridLoaded);
my_gridnode.addEventListener(MouseEvent.CLICK, loadNewsContent);
}
function loadNewsContent(e:MouseEvent):void {
createNewsContainer();
getXMLNewsTarget();
news_category = my_grid[_xmlnewstarget].#category;
var tfnews_category:TextSplash=new TextSplash(news_category,20,16,32,false,false,0xffffff );
tfnews_category.mouseEnabled = false;
newstitle = my_grid[_xmlnewstarget].#newstitle;
var tftitle:TextSplash=new TextSplash(newstitle,20,70,24,false,false,0x333333 );
news_container_mc.addChild(tftitle);
tftitle.mouseEnabled = false;
news_content = my_grid[_xmlnewstarget]..news_content;
var tfnews_content:TextSplash=new TextSplash(news_content,20,110,20,true,true,0x333333,330);
news_container_mc.addChild(tfnews_content);
tfnews_content.mouseEnabled = false;
news_image = my_grid[_xmlnewstarget].#news_image;
loadNewsImage();
addChild(tfnews_category);
addChild(tftitle);
addChild(tfnews_content);
var news_container_mc_tween = new Tween(news_container_mc, "alpha", Strong.easeIn, 0,1,0.3, true);
news_container_mc_tween.addEventListener(Event.INIT, newsContentLoaded);
}
I'm not going to try to read your code (try to work on your formatting, even if it's just indenting), but I'll provide a simplified example:
for (var i = 0; i < my_total; i++) {
var closure = function() {
// use i here
}
}
As you say, when closure is called it will contain the last value of i (which in this case would be my_total). Do this instead:
for (var i = 0; i < my_total; i++) {
(function(i) {
var closure = function() {
// use i here
}
})(i);
}
This creates another function inside the loop which "captures" the current value of i so that your closure can refer to that value.
See also How does the (function() {})() construct work and why do people use it? for further similar examples.
Umm, as mentioned above, the code is a bit dense, but I think you might have a bit of type conversion problem between string and integers, is the "last value" always 0? try making these changes and let me know how you get on.
// replace this gridnode_loader.name = i;
gridnode_loader.name = i.toString();
// explictly type this as an int
_xmlnewstarget = parseInt(my_gridnode.name);
// replace this: my_tweens[Number(my_gridnode.name)] = new Tween(......
my_tweens[parseInt(my_gridnode.name)] = new Tween();
Oh and I think it goes without saying that you should massively refactor this code block once you've got it working.
Edit: after further study I think you need this
//replace this: my_gridnode.addEventListener(MouseEvent.CLICK, loadNewsContent);
var anonHandler:Function = function(e:MouseEvent):void
{
loadNewsContent(_xmlnewstarget);
};
my_gridnode.addEventListener(MouseEvent.CLICK, anonHandler);
Where your loadNewsContent has changed arguements from (e:MouseEvent) to (id:String)
Firstly, you do not need to call addChild for the same loader twice (once in callGrid) and then in (gridLoaded). Then you can try putting inside loadNewsContent: news_category = my_grid[int(e.target.name)].#category;instead of news_category = my_grid[_xmlnewstarget].#category; As _xmlnewstarget seems to be bigger scope, which is why it is getting updated every time a load operation completes.