views:

15

answers:

2

I have a custom Flex Container component written in AS3, called StatisticsContainer. When used in the application, it contains various custom Label components called StatisticsBoxes. So there might be a StatisticsContainer containing 3 StatisticsBoxes: "averageAge", "divorceRate" and "infantMortalityRate".

The StatisticsContainer needs to be able to reference and operate on all the StatisticsBoxes. However I don't want to hard-code the references into StatisticsContainer, as there will be various different instances of StatisticsContainer with different StatisticsBoxes in them.

So how do I dynamically give StatisticsContainer an ArrayCollection of all the StatisticsBoxes it contains?

So far I started with a function like this in the creationComplete of StatisticsContainer:

for (var i:int = 0; i < numElements; i++) {
  if (getElementAt(i) is StatisticsBox) {
    statisticsBoxes.addItem(getElementAt(i));
  }
}

This works only if the StatisticsBoxes are direct subchildren. Anyway it feels a bit hacky.

Then I tried listening for a CreationComplete event in StatisticsContainer. However these don't arrive from StatisticsBox because they don't bubble.

In the end I created my own event which bubbles and I fire it on creationComplete in StatisticsBox, and listen for it in StatisticsContainer. This works, but is this really the best way to do it?

+1  A: 

You can have each StatisticsBox register itself with its StatisticsBoxContainer when it's created.

StatisticsBoxContainer.mxml

<?xml version="1.0"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:local="*">
    <mx:Script><![CDATA[
        import mx.collections.ArrayCollection;

        public var statisticsBoxes:ArrayCollection = new ArrayCollection();

]]></mx:Script>

    <local:StatisticsBox id="stats1" />
    <local:StatisticsBox id="stats2" />
    <local:StatisticsBox id="stats3" />

</mx:Canvas>

StatisticsBox.mxml

<?xml version="1.0"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="onCreationComplete()">
    <mx:Script><![CDATA[

        private function onCreationComplete():void {
            StatisticsBoxContainer(parentDocument).statisticsBoxes.addItem(this);
        }

]]></mx:Script>       
</mx:Canvas>

I'm not sure if this is more or less hacky than looping through children though. The problem with how you're looping now is you need to loop recursively through children, grandchildren, etc.

Sam
A: 

I've had the exact problem in a couple different projects. For speed's sake, I solved it by quickly looping through all the children, exactly as you did, on creation complete and it felt just as "dirty" and "hacky" but it got the job done.

ActionScript isn't as mature as other languages so a lot of solutions require a quick hack because doing it properly would require writing an entire framework, which is impractical.

I've also solved the problem using the Application.application object, which is a cool little trick to get a global reference to things. I use that when I need singleton instances in my application. Again, hacky, yet elegantly simple.

Applying that to your situation, instead of firing an event that has to bubble all over your code you can do something along the lines of the following, whenever a "child box" is created (and your custom event used to be fired):

Application.application.StatisticList.addItem(this);

Another cool trick I use in situations like these is dictionaries instead of ArrayCollections. They give me the ability to do "instant lookup" when I need it while still allowing iteration when necessary. You can iterate over both the keys and the values of a dictionary. The code becomes something like:

Application.application.StatisticList[this.id] = this;

Now you can access your statistics in 3 ways:

  1. Instantly by direct id
  2. In "order," iterating over all ids
  3. In "order," iterating over all StatisticBox objects (the same functionality of ArrayCollection)

I hope some of that helps in some way,

--gMale

gmale