First AS3 Project - How to communicate between custom classes?

Hi everyone!

I’ve started to work with Actionscript 3.0 for a couple of weeks and I’ve just started my very first “top-to-bottom” AS3 project which I’ll be delivering (a “demo”) next Monday.

However, some basic complications are arising and I just don’t know what to do, this probably has been answered a lot of times but I can’t seem to find a single blogpost/forum/website that addresses this issue in a way that I can understand instead of “copy/pasting” code.

Ok, I’ve said enough, let me describe the problem:

Let’s say I have the following classes:

DocumentSetup.as (where all the other class instances are initiated)
NavBar.as (where the website navigation and buttons are placed)
ContentLoader.as (where the website loads external swf’s with content)

Ok, when I press a button inside the NavBar class I want it to execute a function that tells the DocumentSetup generated MovieClip(contentLoader) to load in a different SWF based on the arguments passed on the Event.

My questions are:

1 - How do I access a public function on a different class? Let’s say I have my navFunction() placed on the DocumentSetup Class, do I need to create “another” instance of the DocumentSetup Class just to tell it to do something?

2 - Let’s say you’re able to explain to me how to solve the problem above.
How can I reference a MovieClip generated by the DocumentClass through another Class? I need something to tell the contentLoader MovieClip to change contents, how do I communicate with it from another Class if the MovieClip was added to the DisplayList through code on the DocumentClass?

3 - How can I create a Variable which is identified throughout the website, let’s say I have an XML file with all the text content that’s going to fill in the content placeholder’s. How do I make this XML data accessible from all classes instead of having to re-import the XML again and again?

I know that these are probably some basic questions about OOP but I just don’t know where to turn. My external classes all work 100% themselves but I cannot get them to communicate with each other, I keep getting compiler errors…

A website in which you can’t navigate to a different page is basically useless :stare:

Anyway, sorry for the long post, any help is appreciated guys!
Thanks in advance.

[SIZE=“1”]BTW, If you read this post and have the answer to just one question that I posted, please at least give me that one so I can start figuring this out.[/SIZE]

I’m still getting my head around some of these issues myself, but I’ll have a go at answering this one, hoping that if there are better ways of doing it, someone will tell me . (I certainly don’t claim that my way is the best way.)

Your instance of DocumentSetup which is creating instances of other classes can always pass itself, as a parameter, into the constructors of those other classes.

public class DocumentSetup extends MovieClip
{
   var myNavBar:NavBar = newNavBar(this);
   var myContentLoader:ContentLoader = new ContentLoader(this);

  etc
}

In your NavBar class:




public class NavBar extends MovieClip
{
  var creator:DocumentSetup;

public function NavBar(ds:DocumentSetup):void
{
   creator = ds; 
}
}

Any instance of NavBar will ‘know’ which instance of DocumentSetup created it.

So in your NavBar class, anytime you want to reference the ContentLoader, also created by that instance of DocumentSetup, use

creator.myContentLoader

Another way of doing things would be to use static variables, which can then be addressed by using the name of the class in which they’re declared (rather than any particular instance of that class.) So you could declare a static variable in your DocumentSetup class for that XML document you want to use.

static var theXMLfile:XML

which can then be referenced, from anywhere in your application, with

DocumentSetup.theXMLfile

[QUOTE=DiamondDog;2349784]I’m still getting my head around some of these issues myself, but I’ll have a go at answering this one, hoping that if there are better ways of doing it, someone will tell me . (I certainly don’t claim that my way is the best way.)

Your instance of DocumentSetup which is creating instances of other classes can always pass itself, as a parameter, into the constructors of those other classes.

public class DocumentSetup extends MovieClip
{
   var myNavBar:NavBar = newNavBar(this);
   var myContentLoader:ContentLoader = new ContentLoader(this);

  etc
}

In your NavBar class:




public class NavBar extends MovieClip
{
  var creator:DocumentSetup;

public function NavBar(ds:DocumentSetup):void
{
   creator = ds; 
}
}

Any instance of NavBar will ‘know’ which instance of DocumentSetup created it.

So in your NavBar class, anytime you want to reference the ContentLoader, also created by that instance of DocumentSetup, use

creator.myContentLoader

Another way of doing things would be to use static variables, which can then be addressed by using the name of the class in which they’re declared (rather than any particular instance of that class.) So you could declare a static variable in your DocumentSetup class for that XML document you want to use.

static var theXMLfile:XML

which can then be referenced, from anywhere in your application, with

DocumentSetup.theXMLfile

[/QUOTE]

Wow! Thanks man! That actually makes a lot of sense!

Hmm, I ended up going for the easy way on my most urgent project, I eventually just placed the MovieClips on the Stage and placed the code inside them instead of having them working by external classes. I really have to get this done by Monday.

Still, I’ve got another project I’m working on which has a more flexible time limit, I’m gonna try and make it as close to OOP as I possible can!

The questions are still up for answering though, if someone else wants to jump in the discussion… :rambo:, but your help was already precious! Thanks a lot!

Children should never reference their parents, it’s a fundamental break in encapsulation and class independence. Any interaction in that direction should be done with Events and Listeners - which are both totally independent. If an event fires but is not caught - no problem arises. If a child tries to call a function of its parent, things start getting ugly.

You don’t want to be referencing display objects between classes, that’s just bad practice.

A good way to handle XML that you want accessible from all of your classes is with a Singleton pattern. This is the singleton I use for a lot of my projects:

http://ragona.com/codeexamples/ContentData.as


package  
{
	import com.rragona.utils.coreXML.CoreXML;
	import flash.events.EventDispatcher;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	
	/**
	* ...
	* @author 			rragona
	* 
	* @version 			1.0.0
	* 
	* @history 			[1.0.0] Class created
	* 
	* @description 		This class is a simple data holding singleton that returns 
	* 					XML data. 
	* 
	* @dependencies 	CoreXML
	* 
	*/
	
	public class ContentData extends EventDispatcher
	{
		//:: Singleton Instance
		private static var _instance:ContentData;
		
		//:: Private Vars
		private var _data:XML;
		
		public function ContentData(e:SingletonEnforcer) { }
		
		public static function getInstance():ContentData
		{
			if (_instance == null)
			{
				_instance = new ContentData(new SingletonEnforcer());
			}
			
			return _instance;
		}
		
		public function get data():XML
		{
			return _data;
		}
		
		public function loadData(path:String):void
		{
			if (_data == null)
			{
				_data = new XML();
				
				var cXML:CoreXML = new CoreXML(path);
				cXML.addEventListener(Event.COMPLETE, onDataLoaded);
			}
			else
			{
				throw new Error("The data has already been loaded. The loadData() method should only be called once.");
			}
		}
		
		public function getSection(sIndex:int):XML
		{
			return _data.content.section[sIndex];
		}
		
		public function getSectionData(sIndex:int, setting:String):String
		{
			var retString:String;
			
			if (_data != null)
			{
				retString = _data.content.section[sIndex][setting];
			}
			else
			{
				throw new Error("The XML Data is null. Make sure that the data has been loaded before accessing it.");
			}
			
			return retString;
		}
		
		private function onDataLoaded(e:Event):void
		{
			_data = e.target.xmlData;
			dispatchEvent(new Event(Event.COMPLETE, true));
		}
		
		private function onDataError(e:IOErrorEvent):void
		{
			dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR));
		}
	}
	
}

class SingletonEnforcer {}

It only allows a single load, and is globally accessible - but without the performance hit of having every single method static.

[QUOTE=Anogar;2349859]Children should never reference their parents, it’s a fundamental break in encapsulation and class independence. Any interaction in that direction should be done with Events and Listeners - which are both totally independent. If an event fires but is not caught - no problem arises. If a child tries to call a function of its parent, things start getting ugly.

You don’t want to be referencing display objects between classes, that’s just bad practice.

A good way to handle XML that you want accessible from all of your classes is with a Singleton pattern. This is the singleton I use for a lot of my projects:

http://ragona.com/codeexamples/ContentData.as


package  
{
	import com.rragona.utils.coreXML.CoreXML;
	import flash.events.EventDispatcher;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	
	/**
	* ...
	* @author 			rragona
	* 
	* @version 			1.0.0
	* 
	* @history 			[1.0.0] Class created
	* 
	* @description 		This class is a simple data holding singleton that returns 
	* 					XML data. 
	* 
	* @dependencies 	CoreXML
	* 
	*/
	
	public class ContentData extends EventDispatcher
	{
		//:: Singleton Instance
		private static var _instance:ContentData;
		
		//:: Private Vars
		private var _data:XML;
		
		public function ContentData(e:SingletonEnforcer) { }
		
		public static function getInstance():ContentData
		{
			if (_instance == null)
			{
				_instance = new ContentData(new SingletonEnforcer());
			}
			
			return _instance;
		}
		
		public function get data():XML
		{
			return _data;
		}
		
		public function loadData(path:String):void
		{
			if (_data == null)
			{
				_data = new XML();
				
				var cXML:CoreXML = new CoreXML(path);
				cXML.addEventListener(Event.COMPLETE, onDataLoaded);
			}
			else
			{
				throw new Error("The data has already been loaded. The loadData() method should only be called once.");
			}
		}
		
		public function getSection(sIndex:int):XML
		{
			return _data.content.section[sIndex];
		}
		
		public function getSectionData(sIndex:int, setting:String):String
		{
			var retString:String;
			
			if (_data != null)
			{
				retString = _data.content.section[sIndex][setting];
			}
			else
			{
				throw new Error("The XML Data is null. Make sure that the data has been loaded before accessing it.");
			}
			
			return retString;
		}
		
		private function onDataLoaded(e:Event):void
		{
			_data = e.target.xmlData;
			dispatchEvent(new Event(Event.COMPLETE, true));
		}
		
		private function onDataError(e:IOErrorEvent):void
		{
			dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR));
		}
	}
	
}

class SingletonEnforcer {}

It only allows a single load, and is globally accessible - but without the performance hit of having every single method static.[/QUOTE]

Ok, I still have to read the whole code to figure out what it really means :slight_smile:

Anyway, you mentioned an example of a XML variable, however, how about if I create a new instance of a linked Library symbol (MovieClip extended with it’s own class) in the DocumentClass and then wanted to reference that same MovieClip through a function on another Class. This is what’s troubling me the most.

As always, thanks a lot for the help!

That’s the deal - you shouldn’t ever do that. If you want to reference it in a particular class, create it in that class. You should avoid writing sub-classes that directly reference properties in their parents, it’s just bad news.

Create an event listener in the document class, and dispatch an event from the subclass.