views:

224

answers:

8

I am adding some tabs to the stage, and each tab loads its own swf file. I have an array tracking the tabs, but need to know when each swf is loaded which tab it belongs to. I'm not sure of the best way to do it.

Tabs are controlled by XML, so there can be any number of tabs -

for (var j = 0; j < _xmlTabs.length(); j++) {
    arrTabs[j] = new Tab(j,_xmlTabs[j].attribute("icon"),_xmlTabs[j]);
    addChildAt(arrTabs[j],0);

    mRequest = new URLRequest(_xmlTabs[j].attribute("swf"));  
    mLoader = new Loader();
    addChild(mLoader);
    mLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onTabComplete);
    mLoader.load(mRequest);
}

Ok, so lets say there are 5 tabs being loaded, and each one loads its own swf, and "onTabComplete()" gets fired 5 times. Even though each tab is created in order, onTabComplete may not fire in order because the swfs will be different sizes. So how do I associate each tab with the onTabComplete function? Each instantiated Tab item does have a "position" property which can be retrieved.

+4  A: 

Extend the Loader class with a custom class, add the new property you want, and then you can reference it as

e.currentTarget.loader.newproperty

in the event handler

Jasconius
Are there any resources which show how to do this...I'm familiar with the concept of extending classes, but have never done it
Kim L
It should be in the core Actionscript documentation. Or the first chapter or two of any AS3 book. It's basic OOP.
Jasconius
A: 

I'm not familiar with the Tab class, but does Tab extend a DisplayObjectContainer? you might be able to just do:

arrTabs[j].addChild(mLoader);

and then in the event handler call:

e.currentTarget.parent.position

to get at the indicator of which tab loaded, then handle appropriately perhaps?

JStriedl
What is `parent.position`? `DisplayObjectContainer` doesn't have such a property.
Amarghosh
Thanks, I tried this, but it doesn't really work well to add the swf content to the tab - makes the x and y values messed up.
Kim L
@JStriedl Oops. Only now did I see the `position` property in the question. Please make a minor edit to your answer so that I can remove the down vote (it says vote too old to be changed unless the answer is edited).
Amarghosh
@JStriedl still `e.currentTarget.parent` is not a `Tab`. OP is adding tabs and loaders to the same parent (`this`). Edit the code and I'll remove the down vote.
Amarghosh
I voted up so its back at 0
Allan
edited the question to allow for re-vote.
JStriedl
A: 

Why not identify them with their respective URLs?

private function onTabComplete(e:Event):void
{
  var loaderInfo:LoaderInfo = LoaderInfo(e.target);
  trace(loaderInfo.url);

  //btw, loaderInfo.content is the tab that you are looking for.

  var url:String = loaderInfo.url;
  var tabIndex:Number;
  for(var j:Number = 0; j < _xmlTabs.length(); j++)
  {
    if(url == String(_xmlTabs[j].attribute("swf")))
    {
      tabIndex = j;
      break;
    }
  }
  //tabIndex now contains the required "j"
}

EDIT: In case tabs load same swfs (same urls).

Even if this works, you should consider redesigning.

private function onTabComplete(e:Event):void
{
  int total:Number = _xmlTabs.length();
  var loader:Loader = LoaderInfo(e.target).loader;
  var p:DisplayObjectContainer = loader.parent;
  if(p.numChildren != total * 2)
  {
    trace("other children present : this method won't work");
    return;
  }

  var ldrIndex:Number = p.getChildIndex(loader);
  var tabIndex:Number = ldrIndex - total;
  var matchingTab:Tab = arrTabs[tabIndex];
  //enjoy
}
Amarghosh
updated the code
Amarghosh
This traces out the name of the swf file loaded, which is closer...however two tabs do have the potential to load the same swf file which could be a problem
Kim L
Ok, thanks, ignore my last comment as it refers to the old code...trying this
Kim L
This just doesn't trace the name. After the for loop, the variable `tabIndex` contains the correct value of `j` no matter in what order they load.
Amarghosh
Actually, just looked at the code - same problem, two tabs can load the same swf file. Although...I could change policy so we just need to copy the swf file with a new name and call that instead. It will be a workaround I consider.
Kim L
Two tabs can load the same swf? I gotta admit that I never saw that coming.
Amarghosh
Assuming that you are not adding anything else (other than 5 tabs and 5 loaded swfs) to the parent, the order of children would be `[tab4,tab3,tab2,tab1,tab0,loader0,loader1,loader2,loader3,loader4]`. Since you are passing `j` to the Tab's constructor, you can use getChildIndex to find the matches. I strongly feel that you should consider redesigning this part though.
Amarghosh
is `j` assigned to the `position` property in the Tab constructor?
Amarghosh
`[tab4,tab3,tab2,tab1,tab0,loader0,loader1,loader2,loader3,loader4]` order is because `addChild` adds at the top of the display list (right end of the array) and `addChildAt(tab, 0)` adds at the bottom of the display list (left end of the array). See the updated code.
Amarghosh
Yes - j does get assigned to the position property in the Tab constructor...and I am definitely considering redesigning this to make it the best it can be, just don't quite know how yet :)
Kim L
Did u try the second version of `onTabComplete` that I posted? Is it working as you wanted?
Amarghosh
A: 

AS3 supports anonymous functions as well, so you could do something like this:

addChild(mLoader);
var handleComplete:Function = function(event:Event):void {
    onTabComplete(event, j);
};
mLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, handleComplete);
mLoader.load(mRequest);
thehiatus
Assuming that the loop finishes before any swf is loaded (which it will), the value of `j` inside the anonymous method will always be `_xmlTabs.length()`, the final value of `j`. Test and see it for yourself - that would be a good enough reason for you to avoid anon functions in the future :)
Amarghosh
Amarghosh is correct, I had actually already tried a method like this and the value of j was indeed 5 every time.
Kim L
I believe if you declare another variable inside the for loop and set it equal to j and use that then it will work. That way you're referencing a different variable each time through the loop, so each anon function references a different var, instead of all of them referencing j itself.
Herms
I added an answer that has a tweaked version of this that should work.
Herms
A: 

Modified version of thehiatus's answer that I think should get around the j value being shared issue:

function buildHandleComplete(i:int):Function {
  return function(event:Event):void {
    onTabComplete(event, i);
  };
}

for (var j:int = 0; j < _xmlTabs.length(); j++) {
    arrTabs[j] = new Tab(j,_xmlTabs[j].attribute("icon"),_xmlTabs[j]);
    addChildAt(arrTabs[j],0);

    mRequest = new URLRequest(_xmlTabs[j].attribute("swf"));  
    mLoader = new Loader();
    addChild(mLoader);
    var handleComplete:Function = buildHandleComplete(j);
    mLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, handleComplete);
    mLoader.load(mRequest);
}

By using a function that takes the index value as an argument and builds the listener using that you end up with each closure referencing a different variable, instead of them all referencing the j value.

Herms
what was this technic called again?
Amarghosh
function composition maybe? Not completely sure. It's been a while since I've needed to use any functional programming terminology, and I always mix up the terms.
Herms
Or is it currying? I totally can't remember.
Herms
A: 

in my experience you would want to set up a loading queue. loading them one at a time will prevent a few potential errors

private var j:int = 0; // currentTab number

private function loadNextTab():void{

   arrTabs[j] = new Tab(j,_xmlTabs[j].attribute("icon"),_xmlTabs[j]);
   addChildAt(arrTabs[j],0);

   mRequest = new URLRequest(_xmlTabs[j].attribute("swf"));  
   mLoader = new Loader();
   addChild(mLoader);
   mLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onTabComplete);
   mLoader.load(mRequest);
}
private function onTabComplete():void{
   _xmlTabs[j].attribute("position")
   //Anything else you want to do to instantiate the object.

   currentTab++
   loadNextTab();
}

that doesn't answer your question about passing variables,

it does answer what i suggest you do with the code.

Passing variables:

one common method is "Custom Events"

http://flexcomps.wordpress.com/2008/04/29/dispatching-custom-events-in-as3-with-data-or-object/

create a class that extends Event, then add a data object to the Event. Any data you want to include would then need to be sent to the data object.

some other methods include:

anonymous functions as thehiatus explains

 //errors inside anonymous functions are harder to debug.

and nested functions

private function startLoad():void{
   var j:int = 0;

   //load request calling LoadComplete

   funciton LoadComplete(e:Event){
      //has access to j
      //errors inside nested functions are harder to debug.
   }

}

but these methods would not work well with your for loop either. the data you want to access is reachable by index, but your for-loop blows through the index before any of them finish loading. A load queue is the way to go for your current question.

good luck.

SketchBookGames
A: 

Hi, here is a quick solution ! you can use a Dictionary object.

var d:Dictionary=new Dictionary;
for (var j = 0; j < _xmlTabs.length(); j++) {
    arrTabs[j] = new Tab(j,_xmlTabs[j].attribute("icon"),_xmlTabs[j]);
    addChildAt(arrTabs[j],0);

    mRequest = new URLRequest(_xmlTabs[j].attribute("swf"));  
    mLoader = new Loader();
    //associate a tab with his loader...
    d[mLoader]=arrTabs[j];
    //
    addChild(mLoader);
    mLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onTabComplete);
    mLoader.load(mRequest);
}
//
function onTabComplete(e:Event):void{
    trace("your tab TAB >",d[e.target]);
}
OXMO456
A: 

Associate each loader with the tab (tab has a link to loader), then use the .target member from the event to get the loader that completed and then loop over your tabs to find the tab that has that loader associated.

You'd probably have to create your own tab class if you haven't already to give it a properly to hold the reference to the loader.

I did a similar example of this loading external resources and tracking their progress.

widgisoft