Requirements
Prerequisite knowledge                                     
You should be familiar with ActionScript 3 and object-oriented terminology and principles. Some experience with frameworks is useful, but not required.
 
Required products
Flash Builder (Download trial)
 
User level
Intermediate
 
We are presented with many choices these days when it comes to frameworks for Actionscript 3 development. This is a good sign. The open-source community is alive and vibrant, and tools that make development easier are a good thing. Over the course of the last year, Robotlegs AS3 has seen rapid growth in adoption. It is being leveraged by major media corporations, independent game developers, startups, and enterprises of all sizes.
 
This is the first in a series of Robotlegs articles; subsequent articles will detail core Robotlegs concepts and work through some more advanced concepts involving third-party utilities and libraries built to interact with them.
 
What is Robotlegs?
 
Simply put, Robotlegs is a mechanism for wiring your objects together. ObjectA needs to talk to ObjectB. ObjectA doesn't want, or need, to know that ObjectB even exists. How can they communicate? The simple answer is through Events. With Flash, we have a native event system that is used to facilitate this sort of communication. Likely you use Events on a daily basis. Objects on the display list communicate via events, and event bubbling allows distant objects to receive messages from other display objects. What about objects that aren't on the display list? This is where a framework such as Robotlegs can really make life easier for you.
 
At its core, Robotlegs is a set of modular utilities and interfaces that provide tools to ease these communication tasks, reduce repetitive coding tasks, and manage dependency injection within your application. In addition to this core set of tools, Robotlegs provides a lightly prescriptive MVC+S (Model, View, Controller and Service) implementation to get you started. If you have any experience with PureMVC you will quickly recognize the use of Mediators and Commands within the Robotlegs MVC+S implementation. If not, don't worry, we will be looking at these classes in more depth soon.
 
This article is going to give an overview of Robotlegs through a simple "Hello World" example. You will probably look at the example and say, "Eh? I could have done this in a single MXML file without all the hassle!" While probably true with a trivial example, keep in mind that on a large project this structure quickly becomes invaluable. That is the benefit of development with a framework in general. It allows us to effectively communicate concepts and understand a code base much quicker than a slapped-together application with no patterns and practices.
 
This is not going an exhaustive dissection of Robotlegs, but hopefully it is enough to spark your interest. I've posted resources at the end of the article for further investigation. That said, let's look at some code!
 
Basic structure of a Robotlegs MVC+S application
 
A typical Robotlegs MVC+S application is composed of several parts:
  • Context is the bootstrapping mechanism that initializes dependency injection and the various core utilities that Robotlegs uses.
  • Mediators manage communication between your application's view components and other objects within your application.
  • Commands represent individual actions that your application can perform. These are typically in response to user activity but are not limited to that use case.
  • Models store data and represent the current state of your application.
  • Services are your application's gateway to the outside world.
Let's take a look at the Context and Mediators in more depth, starting with the Context.
 
The Context is at the heart of your application. It provides the central event bus that your other application objects will utilize to communicate with one another. Beyond the initial loading and bootstrapping of your application, you won't deal with the Context during regular development. It comes to life, does its job, and then quietly gets out of your way while you develop your heart out. The Context is not a Singleton. Your application can have any number of Contexts, which makes Robotlegs well-suited for modular applications. We aren't going to explore modular applications here, but it will be the subject of a future article as it is an extremely powerful tool. To start off, let's look at the most basic Context.
 
HelloWorldContext.as
 
import org.robotlegs.mvcs.Context public class HelloWorldContext extends Context { override public function startup():void { //bootstrap here } }
Inside your Context you override the startup method. The startup() method is called when the Context has been fully initialized. Behind the scenes, prior to calling startup(), the Context is creating instances of all the core Robotlegs utilities, preparing to receive dependency injection mappings, and creating the event dispatcher that will be used to communicate among your application's objects.
 
Once your Context class has been created, your application needs a reference to it. In a Flex 4 Spark Application, this is generally done in the main MXML file that extends Application as seen below.
 

HelloWorld.mxml

 

<?xml version="1.0"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:local="*"> <fx:Declarations> <local:HelloWorldContext contextView="{this}"/> </fx:Declarations> </s:Application>

 

Because the Context is a non-visual class, it must be placed in the Declarations tag. You should also note the contextView property bound to the application itself. The context view is the root view of the Context, and is used to provide automated assistance with view component mediation. We will look at that shortly when we discuss the relationships between views and mediators.
 
That is it for the Context. As I mentioned, it has a very brief—but critical—role in the lifecycle of your application. With your Context ready, we can now add a few view components and get them speaking to one another via Robotlegs. Let's take a look at Mediators and their relationship to your application's view components now.
 
Mediators sit between your view components and the rest of the application. Put simply, Mediators listen for events. Your view components dispatch events when the user interacts with them or they are updated in some other way. These events need to be captured and delivered to the rest of the application. Perhaps the user has clicked a Save button and some information needs to get sent to a server. The mediator listens for this event to occur and, when it does, the mediator gathers the appropriate information and sends an event that the rest of the application can utilize to perform some unit of work on the data. Likewise, a Mediator also listens to events from the rest of the application. If some data has been received from the server, parsed, and an event dispatched from a service class, the Mediator is the place where you will listen for that event and update its view component with the new data. Here is a view that will receive a Mediator.
 
MessageView.mxml
 
<?xml version="1.0"?> <s:TextArea xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx"> </s:TextArea>

 

This class is short. It is nothing more than a simple extension of a TextArea. Why not just use a TextArea? Dependency injection works better with unambiguous classes. What this means is that by extending TextArea into our new MessageView class, we are creating a specific view component for the dependency injection to act upon. This is important if our application were to have several TextAreas that served different purposes. By dividing up our classes in this way, we are clearly defining the intent of the class and allowing for the dependency injection tools to do their jobs effectively. With the MessageView, we might also add some additional functionality in the future. For this example, it will remain a simple TextArea, but you get the idea. Now we'll look at the mediator for the MessageView component.
 
MessageViewMediator.as
 
import org.robotlegs.mvcs.Mediator; public class MessageViewMediator extends Mediator { [Inject] public var view:MessageView; override public function onRegister():void { trace("I am registered!"); } }
The MessageViewMediator has two interesting features at this point. Right away, you will notice the first use of the [Inject]metadata tag. This tag is used by Robotlegs to identify properties and methods that it needs to perform injection on. With a mediator, the mediated view is always available for injection when the mediator is created. You don't need to make any special consideration for mapping the view for injection. That is taken care of for you when you map the view to its mediator. We will look at that in a moment, but first let's take a look at the second interesting feature of the basic mediator which is the onRegister() method.
 
The onRegister() method is the hook provided to you when the mediator has been fully initialized. The injections have occurred, the view is ready, and it is where you typically add your event listeners for both the view component and the application. You will typically override this method in every Robotlegs mediator you create.
 
Now that you have a view component and a mediator, they need to be registered, or mapped, with the Context. This is achieved through the MediatorMap. As the name suggests, the MediatorMap is a utility for mapping mediators to view components within the Context. In addition the MediatorMap by default listens to the contextView for ADDED_TO_STAGE and REMOVED_FROM_STAGE events to automatically create and destroy mediators as their corresponding view components are added and removed from the display list. It is worth noting that in graphics-intensive applications with many (thousands of) display objects, this automatic mediation can be a performance issue. In a typical application it is very convenient and rarely causes performance issues. Here is how we map the MessageView to its MessageViewMediator within the HelloWorldContext.
 
override public function startup():void { mediatorMap.mapView(MessageView, MessageViewMediator); }

 

With that done, all that is left is to add the MessageView to the HelloWorld.mxml.
 
HelloWorld.mxml
 
<?xml version="1.0"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:local="*"> <fx:Declarations> <local:HelloWorldContext contextView="{this}"/> </fx:Declarations> <local:MessageView top="40" width="100%" height="100%"/> </s:Application>
 
Now, when you run your application in the debugger, you will see "I am registered!" printed to the console. Congratulations, you have a fully functional Robotlegs application! Granted, that functionality is limited to almost nothing right now, but we can change that. To give our application something to do besides just bootstrap, we will add a button called HelloButton that simply extends the Spark Button class, as well as a mediator for HelloButton called... HelloButtonMediator.
 
HelloButton.mxml
 
<?xml version="1.0"?> <s:Button xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx"> </s:Button>

 

HelloButtonMediator.as
 
import flash.events.MouseEvent; import org.robotlegs.mvcs.Mediator; public class HelloButtonMediator extends Mediator { [Inject] public var view:HelloButton; override public function onRegister():void { } }
 
Right now the HelloButtonMediator looks exactly like the MessageViewMediator above, except the classes are different. Your Context startup method will look like this once you have added the mapping for the HelloButton and its mediator.
 
override public function startup():void { mediatorMap.mapView(MessageView, MessageViewMediator); mediatorMap.mapView(HelloButton, HelloButtonMediator); }
You will also add the button to HelloWorld.mxml so that it will be added to the display list. You will probably want to add something wittier to the HelloButton's label property, but I will leave that up to you.
 
HelloWorld.mxml
 
<?xml version="1.0"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:local="*"> <fx:Declarations> <local:HelloWorldContext contextView="{this}"/> </fx:Declarations> <local:HelloButton label="Say Hello"/> <local:MessageView top="40" width="100%" height="100%"/> </s:Application>
 
At this point we have two fully mediated view components that are just dying to talk. I'm not one to deny my objects their desires, so let's do just that—starting with the custom event they will use to communicate.
 
HelloWorldMessageEvent.as
 
public function get message():String { return _message; } public function HelloWorldMessageEvent(type:String, message:String, bubbles:Boolean = false, cancelable:Boolean = false) { super(type, bubbles, cancelable); _message = message; } override public function clone():Event { return new HelloWorldMessageEvent(type, message, bubbles, cancelable) }
This is a simple custom event. Be sure to override the clone() method in your custom events. Events cannot be redispatched, relayed, or bubbled without this method. I make it a habit to always override clone() in all my custom events. After being burned with a couple hours of head-scratching debugging, you will too.
 
What we want to do is update the MessageView when the user clicks the HelloButton. The HelloButtonMediator needs to listen for the MouseEvent.CLICK on the HelloButton and then dispatch the HelloWorldMessageEvent to the application. It doesn't know who will respond to this event. It doesn't care who responds to the event. The HelloButtonMediator has done its job.
 
HelloButtonMediator.as
 
public class HelloButtonMediator extends Mediator { [Inject] public var view:HelloButton; override public function onRegister():void { addViewListener(MouseEvent.CLICK, handleMouseClick) } private function handleMouseClick(event:MouseEvent):void { dispatch(new HelloWorldMessageEvent(HelloWorldMessageEvent.MESSAGE_DISPATCHED, "Hello World")); } }
With the view listener added to the HelloButtonMediator, we are now dispatching an event to the application. The next step is to do something with that event. The MessageViewMediator seems like the logical choice.
 
MessageViewMediator.as
 
public class MessageViewMediator extends Mediator { [Inject] public var view:MessageView; override public function onRegister():void { addContextListener(HelloWorldMessageEvent.MESSAGE_DISPATCHED, handleMessage) } private function handleMessage(event:HelloWorldMessageEvent):void { view.text = event.message; } }
...and with that, we have a Hello World! Seems like the long way to get there, but it will pay off in your non-trivial applications. In the next part in this series we will explore models and commands followed by an article on services. In addition to the core of Robotlegs, we will look in detail at some of the utilities that are currently available to make use of tools such as AS3-Signals and modular application development. It is an exciting time to be developing with Robotlegs.
 
There are articles on my blog (and lots of others across the internets) dealing with various Robotlegs topics. John Lindquist has a Hello World screencast on his blog (watching him use FDT is interesting in and of itself). Additionally there is a best-practices document that has proven helpful for many. You can always hit the Robotlegs Knowledge Base for help and support. It has an active group of community volunteers, including myself, that diligently answer questions regarding all things Robotlegs. For an in-depth discussion of these topics and more, see ActionScript Developer's Guide to Robotlegs.
 
Up next: Part 2: Models.