A Flash Developer Resource Site

Results 1 to 3 of 3

Thread: Building an array that dispatches Event.CHANGE

  1. #1
    Multitouch Aficionado
    Join Date
    Mar 2006
    Posts
    275

    Building an array that dispatches Event.CHANGE

    I'm trying to build an Array subclass that dispatches an event when it is modified. I have tried two different methods and run into problems:

    extending Array:
    -direct access (myArray[1] = 5;) can happen without dispatching the event

    extending Proxy:
    -for (var i in myArray) breaks because nextName is typed as a String, not an int
    -Array is no longer compatible with Superarray in that passing a Superarray into something expecting an Array will throw an error.

    Suggestions?


    Source:
    Code:
    //The one that extends Array:
    
    package {
    	import flash.events.Event;
    	import flash.events.EventDispatcher;
    	import flash.events.IEventDispatcher;
    
    	
    		//Just like an array, but it dispatches events when it's changed.
    	public dynamic class Superarray extends Array implements IEventDispatcher{
    		private var dispatcher:EventDispatcher;
    		
    		public function Superarray(... args) {
    			dispatcher = new EventDispatcher(this);
    			
    			if (args.length > 0)
    				super(args);
    		}
    		
    		private static function _megaPop(source:Array, begin:uint, end:uint = uint.MAX_VALUE):Array {
    			//Broken out into static function because source is redefined during function.
    			
    			if (end == uint.MAX_VALUE)
    				end = begin;
    			
    				//Cuts values begin...end from middle of source and returns them
    			var part1 = source.slice(0, (begin-1));
    			var part2 = source.slice((1+end), (source.length-1));
    			
    			var cutOut = source.splice(begin, (end-begin+1));
    			
    			source = part1.splice(0, 0, part2);
    			
    			return (cutOut);
    		}
    		
    		public function megaPop(begin:uint, end:uint = uint.MAX_VALUE):Array {
    			return _megaPop(this, begin, end);
    		}
    		
    		AS3 override function slice(startIndex = 0, endIndex = 0xFFFFFF):Array {
    			if (endIndex >= this.length)
    				endIndex = this.length;
    				
    			var results:Superarray = new Superarray();
    			for (var i:int = startIndex; i < endIndex; i++) {
    				results.push(this[i]);
    			}
    			return results;
    		}
    		
    		AS3 override function push(... args):uint {
    			var results = super.push.apply(this, args);
    			dispatchEvent(new Event(Event.CHANGE));
    			return results;
    		}
    		
    		AS3 override function pop() {
    			var results = super.pop();
    			dispatchEvent(new Event(Event.CHANGE));
    			return results;
    		}
    		
    		AS3 override function reverse():Array {
    			var results = super.reverse();
    			dispatchEvent(new Event(Event.CHANGE));
    			return results;
    		}
    		
    		/*AS3 override function splice(startIndex:int, deleteCount:uint, ... args):Array {
    			var results = super.splice(args);
    			dispatchEvent(new Event(Event.CHANGE));
    			return results;
    		}*/
    		
    		AS3 override function shift() {
    			var results = super.shift();
    			dispatchEvent(new Event(Event.CHANGE));
    			return results;
    		}
    		
    		AS3 override function unshift(... args):uint {
    			var results = super.unshift.apply(this, args);
    			dispatchEvent(new Event(Event.CHANGE));
    			return results;
    		}
    			   
    		public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void{
    			dispatcher.addEventListener(type, listener, useCapture, priority);
    		}
    			   
    		public function dispatchEvent(evt:Event):Boolean{
    			return dispatcher.dispatchEvent(evt);
    		}
    		
    		public function hasEventListener(type:String):Boolean{
    			return dispatcher.hasEventListener(type);
    		}
    		
    		public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void{
    			dispatcher.removeEventListener(type, listener, useCapture);
    		}
    					   
    		public function willTrigger(type:String):Boolean {
    			return dispatcher.willTrigger(type);
    		}
    	}
    }
    
    
    //The one that extends Proxy:
    
    package {
    	import flash.events.Event;
    	import flash.events.EventDispatcher;
    	import flash.events.IEventDispatcher;
    	import flash.utils.Proxy;
    	import flash.utils.flash_proxy;
    
    	
    		//Just like an array, but it dispatches events when it's changed.
    	public dynamic class Superarray extends Proxy implements IEventDispatcher{
    		private var dispatcher:EventDispatcher;
    		private var _array:Array;
    		
    		public function get array():Array { 
    			return _array;
    		}
    		
    		public function set array(value:Array):void {
    			_array = value;
    		}
    
    		public function Superarray(... args) {
    			dispatcher = new EventDispatcher(this);
    			_array = new Array();
    			
    			
    			for each (var thisArg in args) {
    				_array.push(thisArg);
    			}
    		}
    
    		override flash_proxy function callProperty(methodName:*, ... args):* {
    				//call function on _array
    			var result;
    			
    				//if array has changed, dispatch change event
    			switch (methodName.toString()) {
    				case 'slice':
    					result = new Superarray();
    					result.array = _array.slice();
    					break
    				case 'push':
    				case 'pop':
    				case 'reverse':
    				case 'splice':
    				case 'shift':
    				case 'unshift':
    					result = _array[methodName].apply(_array, args);
    					this.dispatchEvent(new Event(Event.CHANGE));
    					break;					
    			}
    			
    			return result;
    		}
    
    		override flash_proxy function getProperty(name:*):* {
    			return _array[name];
    		}
    
    		override flash_proxy function setProperty(name:*, value:*):void {
    			_array[name] = value;
    			this.dispatchEvent(new Event(Event.CHANGE));
    		}
            
            // nextNameIndex called when the loop starts
            override flash_proxy function nextNameIndex (index:int):int {
                if (index < _array.length) {
                    // first call is 0, return 1 + index the index
                    // starts with 1, then 2, then 3... etc.
                    return index + 1;
                } else {
                    // after outside of _array bounds,
                    // stop the loop returning 0
                    return 0;
                }
            }
            
            // nextName called after nextNameIndex returns non-zero
            override flash_proxy function nextName(index:int):String {
                // return the array item in index - 1
                // this relates to a property name in a for..in
                return index.toString();// _array[index - 1];
            }
            
            // nextValue called after nextNameIndex returns non-zero
            override flash_proxy function nextValue(index:int):* {
                // return the array item in index - 1
                // this relates to a property value in a for each..in
                return _array[index - 1];
            }
    			   
    		public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void{
    			dispatcher.addEventListener(type, listener, useCapture, priority);
    		}
    			   
    		public function dispatchEvent(evt:Event):Boolean{
    			return dispatcher.dispatchEvent(evt);
    		}
    		
    		public function hasEventListener(type:String):Boolean{
    			return dispatcher.hasEventListener(type);
    		}
    		
    		public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void{
    			dispatcher.removeEventListener(type, listener, useCapture);
    		}
    					   
    		public function willTrigger(type:String):Boolean {
    			return dispatcher.willTrigger(type);
    		}
    	}
    }

  2. #2
    Senior Member cancerinform's Avatar
    Join Date
    Mar 2002
    Location
    press the picture...
    Posts
    13,449
    From what I see this part is never executed:
    if (endIndex >= this.length)
    endIndex = this.length;

    for (var i:int = startIndex; i < endIndex; i++) {
    results.push(this[i]);

    }

    traces:
    trace("EI:"+endIndex);
    trace("TL:"+this.length);

    give:
    EI:-1
    TL:1
    EI:0
    TL:1

    This is what I have in my fla:
    var aa:Superarray=new Superarray("apples","pears","oranges","tomatoes");
    aa.addEventListener(Superarray.ARRAY_CHANGED,cHand ler);
    function cHandler(event:Event):void
    {
    trace("yes");
    }
    aa.megaPop(0,1);
    If you dispatch an event before it is in fact dispatched. You want to look at your _megaPop function.
    When I replaced "this" by a static var _args:Array to hold args the megaPop function is not executed. I changed because this.length should give 4 but gives only 1.

    I hope I understood things correctly.


    I am not sure what you want to achieve there.
    - The right of the People to create Flash movies shall not be infringed. -

  3. #3
    Multitouch Aficionado
    Join Date
    Mar 2006
    Posts
    275
    I didn't consider using a negative number when I reimplemented slice. Good catch.

    As far as megaPop, that's something I had written before and used as an external static function. I added it to Superarray right before I posted here. Looks like I forgot to add the Event.CHANGE.

    Thanks for those.


    What I'm really interested in is dispatching an Event.CHANGE when someone calls myArray[5] = something; I can do that in the Proxy class, except it breaks the for...in loop and backwards compatibility with the Array class. It'd be ideal if I could make the version that extends Array dispatch the event if the array access operator is used to modify the Array, but I don't know if that's possible. If not, it would also be nice if I could make Proxy.nextName return an int instead of a String so that implementation wouldn't break the for...in loop.

    I'm bumping into what might be limitations of the language. I'm looking for suggestions to work around them. At the end of the day, all I care about having is an Array class that dispatches CHANGE events when it is changed.

    Thanks again!

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  




Click Here to Expand Forum to Full Width

HTML5 Development Center