patrickhoehn
patrickhoehn

Reputation: 25

AS3: Access variable outside of function

This issue is driving me nuts and I don't know what I'm missing here. This is my code:

package  {
	
	import flash.display.*;
	import flash.text.*;
	import flash.events.*;
	import flash.net.*;
	import flash.xml.*; 
	
	public class PopUp extends Sprite {
		
		public var container:Array = new Array();

		public function PopUp() {
			
			var contentXML:XML = new XML(); 
			var XML_URL:String = "./content/content.xml"; 
			var contentXMLURL:URLRequest = new URLRequest(XML_URL); 
			var contentLoader:URLLoader = new URLLoader(contentXMLURL); 
			contentLoader.addEventListener("complete", FuncXML);
			
			function FuncXML(event:Event):void { 
				contentXML = XML(contentLoader.data);
				for each (var item in contentXML.item) {
					
					var txtbox:Sprite = new Sprite();
					txtbox.graphics.beginFill(0x444444, 0.9);
					txtbox.graphics.drawRoundRect(0, 0, 100, 100, 15, 15)
					txtbox.graphics.endFill();
					txtbox.name = "txtbox_"+item.settings.target;
					addChild(txtbox);
					container.push(txtbox);
				}
				trace(container.length); // Returns 2
			}
		trace(container.length); // Returns 0
		}

	}
	
}

The final version of this code should create text boxes from the contents of an xml file, but I can't get the content out of the function "FuncXML". I tried this with trace. The trace inside the function returns the correct length of "container", which is 2. Outside the function, the trace returns 0. Could you please tell me what's wrong here?

Upvotes: 0

Views: 186

Answers (3)

Nbooo
Nbooo

Reputation: 865

As it was explained in other answer, your second trace will be executed before your first trace in the inner function. I happens because URLLoader is not instant, some time is needed for data to load. So you must wait until it finishes the work. You can wait for specific events as described in this document: URLLoader Adobe Help. Below I can suggest some basic example of what you might want to do:

public class PopUp extends Sprite {

    public var container:Array = new Array();
    // Class constructor       
    public function PopUp() {

    }

    public function prepareAndLoad():void {
        var XML_URL:String = "./content/content.xml"; 
        var contentXMLURL:URLRequest = new URLRequest(XML_URL); 
        var contentLoader:URLLoader = new URLLoader(); 
        contentLoader.addEventListener(Event.COMPLETE, onXMLLoadComplete);
        // good practice here would be to expect errors as well:
        contentLoader.addEventListener(IOErrorEvent.IO_ERROR, onError);
        contentLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);

        contentLoader.load(contentXMLURL);
    }
    private function onError(e:ErrorEvent):void {
        trace("Error");
        // maybe you want to handle this situation somehow.
    }

    private function onXMLLoadComplete(event:Event):void { 
        var contentXML:XML = XML(event.target.data);
        for each (var item in contentXML.item) {
            var txtbox:Sprite = new Sprite();
            txtbox.graphics.beginFill(0x444444, 0.9);
            txtbox.graphics.drawRoundRect(0, 0, 100, 100, 15, 15)
            txtbox.graphics.endFill();
            txtbox.name = "txtbox_"+item.settings.target;
            addChild(txtbox);
            container.push(txtbox);
        }
        trace(container.length);
        // here your XML is loaded and processed. Dispatch an event to notify that job is done and container is not empty.
        dispatchEvent(new Event(Event.COMPLETE));
    }
}

Somewhere in your project you can create an instance of this popup:

//...
var popUp:PopUp = new PopUp();
popUp.addEventListener(Event.COMPLETE, onPopUpReady);
popUp.prepareAndLoad();
//...
private function onPopUpReady(e:Event):void {
    var popUp:PopUp = e.target as PopUp;
    trace(popUp.container.length); // Ready and loaded.
} 
//...

Upvotes: 3

cameraman
cameraman

Reputation: 252

As mentioned before, you are looking for the items before they are loaded.
You should wait until they are loaded and ready, and then handle them on the stage or elsewhere.

I would handle this situation like this:
On your main file:

var newTxt:PopUp = new PopUp({
    onReady_func: handlePopUp //pass the function to PopUp class as a parameter
}); 

// this function will be called whenever PopUp is ready
function handlePopUp(){
    trace(newTxt.container.length);
}

In the PopUp class:

...
// the $config object contains setup parameters 
public function PopUp($config) {
    ...
    function FuncXML(event:Event):void { 
       ...
       //  Everything is ready. Lets tell them.
       $config.onReady_func(); // this call should be in the last line of funcXML
    }

Upvotes: -1

Andrea Rega
Andrea Rega

Reputation: 371

Reading your code I can see that the second trace(container.length); returns 0 simply because it is called before the "first" trace(container.length);.

You can understand it if you see that the function FuncXML is only declared and added as listener callback (or handler) before the second trace, but since you are in an asynchronous context you will never know if the FuncXML content will be executed before the second "trace" call.

In particular you have two functions flows that runs in parallels: PopUp flow FuncXML flow, but you are assuming that they are consequential.

Hope this could help you

Upvotes: 2

Related Questions