3

I'm sort of jumping in headfirst to some Flex/AIR stuff. I have a pretty solid background with AS3, but given the inherent hierarchal complexity of Flex (compared to regular Flash), I'm running into an issue.

Let's assume that you have an app where pretty much everything is event driven (common). Accessing elements in the near vicinity of the event target, or the event target itself, is trivial. I'm trying to find, however, the most practical (read: best, most efficient) way to find children that are far removed from the current context.

I know there are functions like getChildAt() and getChildByName(), but that assumes a parent context; what if the element (Flex) you're looking for is several parents up, in a sibling, and then several children down? We take for granted things like jQuery that do this easily, but obviously we don't have that luxury in AS3.

Are any of the following valid? Is there a better way?

  1. Iterate through parents and parents' parents until you find a stop point, find the sibling, and iterate through children and their children until you find your target;

  2. Keep key objects in a global object store (sic) and reference them as necessary (yech)

  3. Use specific dot notation to reach the target, including elements (like skins and their containers - yech again)

Any thoughts would be appreciated.

Edit:

To clarify, let's take an empty Flex 4 AIR app. We have WindowedApplication as the root, obviously, and let's add two SkinnableContainer children with IDs navContainer and mainContainer, respectively. Both have custom skins. Within mainContainer, we have another SkinnableContainer with a vertical layout and ID mainContent, and as one of its children, it has an object (any will do - a spark BorderContainer, maybe) with the ID animatedBox, for example. Within the navContainer, we have a spark Button, which has a listener bound for MouseEvent.CLICK. Within that function, we are going to want to access animatedBox (nativeWindow.mainContainer.mainContent.animatedBox) and animate it to change, say, it's width.

The goal is to access that distant DisplayObject (animatedBox) in a way that is as unobtrusive and efficient as possible, while still conforming to Flex standards that I clearly have yet to possess. :)

mway
  • 4,334
  • 3
  • 26
  • 37
  • so are you looking for the best way to just pass events to objects far away in the display list or you need to create a kind of map of your display list? – www0z0k Oct 28 '10 at 06:31
  • why are you actually wanting to do this? Remember that AS3 is more OO than JS, so there is almost certainly a better OO way of approaching your problem than one of the 3 methods listed. – Gregor Kiddie Oct 28 '10 at 09:58
  • i can not understand your question. However, Make an array which will store references to all objects used in swf and that array will be multi-dimensional. for example stage has three children and child 2 has further 3 children then you can use removechild(GlobalRef[1][0]) to remove the child 1 of the stage 2nd child. means nest the reference as the objects are nested. – Muhammad Irfan Oct 28 '10 at 11:47
  • if the question is about how to listen to events from objects far away in the display list i have the solution – www0z0k Oct 28 '10 at 12:03
  • @www0z0k: I'm not looking to pass events specifically; I'm looking to access them so that I can either bind event listeners or, more importantly, change properties on them. – mway Oct 28 '10 at 14:51
  • @Gregor Kiddie: Oh definitely. I'm very comfortable with AS3, but it doesn't seem like Flex has the same ease of traversing a hierarchal display structure as regular Flash does; would it be easier if I were using the `name` attribute instead of `id`? – mway Oct 28 '10 at 14:53
  • Usually because Flex works to more modern standards than Flash. What's the use case you are actually trying to solve so we can attack it in a more "Flexy" way. – Gregor Kiddie Oct 28 '10 at 15:06
  • @mway: i use a custom Radio class with static functions addListener, killListener and broadcast so i don't have to care about display list hierarchy in my event model. would it help you? – www0z0k Oct 28 '10 at 15:31
  • @www0z0k: Hmm... I wrote an event management class as well, which stores callbacks by name and maps them to whichever elements they're bound to (which is unnecessary, really, its only real purpose is to have an interface for binding them). I'd be happy to take a look, though. – mway Oct 28 '10 at 15:38

3 Answers3

1

in my implementation it is easy to do (however it's in pure AS3):

in display object which handles the click:

private function onClick(e:MouseEvent):void{
    Radio.broadcast(new CustomEvent(id, ..params));
}

in animatedBox:

Radio.addListener(id, new Reciever(uid, animate));

private function animate(e:CustomEvent) {
   //needed code and access of CustomEvent props you pass
}

upd:

package lazylib.broadcast 
{
    /**
     * ...
     * @author www0z0k
     */
    public class Reciever 
    {
        private var id: String;
        private var toRun: Function;
        /*@param nm - unique listener id - required
         *@param fn - event handler function - required*/
        public function Reciever(nm:String, fn:Function) 
        {
            id = nm;
            toRun = fn;         
        }

        public function onEvent(e:* = null):String {
            if (e == null) { return id; }
            toRun(e);
            return id;
        }

        public function get ID():String { return id; }

    }

}

and

package lazylib.broadcast
{
    import flash.events.Event;
    import flash.events.EventDispatcher;
    /**
     * ...
     * @author www0z0k
     */
    public final class Radio extends EventDispatcher
    {
        private static var listeners: Object = new Object();
        private static var archive: Array = new Array();
        private static var forSlowpokes: Object = new Object();

        public static function get ForSlowpokes():Object { return forSlowpokes; }

        public static function addListener(type: String , listener: Reciever):Boolean {
            listeners['anchor'] = null;
            if (!listeners[type]) { 
                var o: Object = new Object();
                listeners[type] = o;
            }
            if (!listeners[type][listener.ID]) {
                listeners[type][listener.ID] = listener; 
                return true;
            }else {
                return false;
            }
        }

        public static function broadcast(evt: * , singleUse:Boolean = false):void {
            var type:String = (evt as Event).type;          
            if (listeners[type]) {
                var returned: Array = new Array();
                for (var i: String in listeners[type]) {
                    if(listeners[type][i]){
                        var fnRetVal: String = listeners[type][i].onEvent(evt);
                        returned.push(fnRetVal);
                    }else{
                        //trace("no listener for id = " + i + ' , type = ' + type);
                    }
                }

            }else {
                //trace("nobody's interested in : \"" + type + "\"");
            }
            if (singleUse) {
                forSlowpokes[type] = 'you missed it realtime';
                delete listeners[type];
            }
        }

        public static function clearDeadFuncs(namez:Object):void {
            for (var a:String in namez) {
                if (a != 'anchor') {
                    killListener(a, namez[a]);
                }
            }
        }

        public static function killListener(type: String , id: String):Boolean {
            if (!listeners[type]) { 
                //trace("there are no listeners for event : " + "\"" + type + "\"");
                return false;
            }else {
                if (!listeners[type][id]) {
                    //trace("there is no \"" + id + "\" listener for event : " + "\"" + type + "\"");
                    return false;
                }else {
                    listeners[type][id] = null;
                    //trace("removed listener \"" + id + "\" for event : " + "\"" + type + "\"");
                    var evt2kill: Number = 0;
                    for (var str: String in listeners[type]) {
                        if (listeners[type][str]) {
                            evt2kill++;
                        }
                    }
                    if (evt2kill == 0) {
                        delete listeners[type];
                        //trace("no more listeners for event : " + "\"" + type + "\"");
                        return true;
                    }
                    return true;
                }
            }
        }
    }
}

delivered as is ;)

www0z0k
  • 4,444
  • 3
  • 27
  • 32
  • I ended up realizing that all elements with IDs are available via dot notation from a reference with the main window; but this is pretty handy, so I'll accept your answer for the suggestions. :) Thanks! – mway Nov 01 '10 at 00:59
0

We take for granted things like jQuery that do this easily, but obviously we don't have that luxury in AS3.

well there is this: http://tech.nitoyon.com/blog/2008/01/as3query_alpha.html

Daniel
  • 34,125
  • 17
  • 102
  • 150
  • Looks interesting. Without having looked at it too seriously, though, I'm wondering about performance and how much overhead a larger app would incur using it. – mway Oct 29 '10 at 06:24
0

I asked myself this question also a lot of times. Still haven't figured out an ultimate solution to the problem. Iterating through parents and parents is definately a way but has to be taken with caution, cause relations might change in your application during runtime. I wrote a simple method a few days ago that lets you iterate through all parents of a given object. Definitely not an elegant solution but it works so far. the SWIZ framework also offers good methods to facilitate the communication between objects via code injection and Event mediation. Maybe worth a look...

Chris
  • 116
  • 1
  • 6