Skip to content
Pete Shand edited this page Mar 6, 2019 · 2 revisions

OpenFL-Robotlegs is a Haxe application framework for the HTML5 and Flash targets, It has been ported from AS3 Robotlegs v2.2.1 It offers:

  • Dependency injection
  • Module management
  • Command management
  • View management
  • Plug-and-play extensions

Installing Robotlegs

Once you have haxelib and openfl installed and working correctly we can pull down robotlegs from haxeliB:

haxelib install robotlegs

This will pull robotlegs and it's dependencies (swiftsuspenders) and make them available from within your project. or download via Github: https://github.com/robotlegs/openfl-robotlegs-framework A few very basic FlashDevelop example projects can also be downloaded:

haxelib install robotlegs-examples

Windows Only:

and FlashDevelop projects template files can be downloaded:

haxelib install robotlegs-templates

Once the robotlegs-templates have finished downloading, navigate to the haxelib folder: Eg: something like C:\HaxeToolkit\haxe\lib\robotlegs-templates and then run the copy.bat file. This will copy the template files to the appropriate folder.

Documentation & Support

The framework documentation exists as README files in the repository. The best way to read them is through GitHub: Documentation Support

Robotlegs 2 (What's New?)

  • The fluent API makes your code more readable
  • An extension mechanism makes the framework highly customizable
  • Bundles allow you to get applications started quickly
  • Lightweight, built-in logging shows you what the framework is doing
  • Mediators can be mapped to abstract types and interfaces
  • View processing has been significantly optimized
  • A context can process more than one display list (PopUp support)
  • Greatly simplified module (multi-context) support

Quickstart

Examples

Probably the easiest way to get up and running is to download and alter the example. These can be found at: https://github.com/robotlegs/openfl-robotlegs-framework-examples

Meta Data

OpenFL-Robotlegs relies on the meta reflection for dependency injection. This means that any class that needs objects injected into it should implement DescribedType (or extend a class that implements DescribedType).

import org.swiftsuspenders.utils.DescribedType;

class ClassName implements DescribedType
{
   @inject public var injectedObject:SomeType;
   public function new() { }
   ...
}

Dead Code Elimination

Because OpenFL-Robotlegs heavily relies on Reflection, the dead code elimination often mistaken removes classes that are actually needed. To combat this it is recommended to use the @:keep or @:keepSub meta data tag to inform the dead code eliminator to ignore these classes.

@:keepSub
class ClassName
{
   ...
}

Creating A Context

To create a Robotlegs application or module you need to instantiate a Context. A context won't do much without some configuration. Plain Haxe:

_context = new Context()
   .install(MVCSBundle)
   .configure([MyAppConfig, SomeOtherConfig])
   .configure(new ContextView(this));

We install the MVCSBundle, which in turn installs a number of commonly used Extensions. We then add some custom application configurations. We pass the instance "this" through as the "contextView" which is required by many of the view related extensions. It must be installed after the bundle or it won't be processed. Also, it should always be added as the final configuration as it may trigger context initialization. Note: You must hold on to the context instance or it will be garbage collected. Framework

Context Initialization

If a ContextView is provided the Context is automatically initialized when the supplied view lands on stage. Be sure to install the ContextView last, as it may trigger context initialization. If a ContextView is not supplied then the Context must be manually initialized.

_context = new Context()
   .install(MVCSBundle)
   .configure([MyAppConfig, SomeOtherConfig])
   .initialize();

ContextView

Application & Module Configuration

I tend to break my config classes up, so I have one for Commands, Models, Services and Views/Mediators, but for the purpose of a demonstration I'll combine everything into one config class. A simple application configuration file might look something like this:

package com.imagination.robotlegs.basic.commands;

import com.imagination.robotlegs.basic.events.AppEvent;
import robotlegs.bender.extensions.eventCommandMap.api.IEventCommandMap;
import robotlegs.bender.framework.api.IConfig;
import robotlegs.bender.framework.api.IInjector;
import com.imagination.robotlegs.basic.commands.example.ExampleCommand;

@:keepSub
class CommandConfig implements IConfig implements DescribedType
{
   @inject public var injector:IInjector;
   @inject public var mediatorMap:IMediatorMap;
   @inject public var commandMap:IEventCommandMap;
   @inject public var contextView:ContextView;

   public function new() { }

   public function configure():Void
   {
      // Map UserModel as a context enforced singleton
      injector.map(UserModel).asSingleton();

      // Create a UserProfileMediator for each UserProfileView
      // that lands inside of the Context View
      mediatorMap.map(UserProfileView).toMediator(UserProfileMediator);

      // Execute UserSignInCommand when UserEvent.SIGN_IN
      // is dispatched on the context's Event Dispatcher
      commandMap.map(UserEvent.SIGN_IN).toCommand(UserSignInCommand);

      // The "view" property is a DisplayObjectContainer reference.
      // If this was a Flex application we would need to cast it
      // as an IVisualElementContainer and call addElement().
      contextView.view.addChild(new MainView());
   }
}

The configuration file above implements IConfig. An instance of this class will be created automatically when the context initializes. We Inject the utilities that we want to configure, and add our Main View to the Context View. Framework

An Example Mediator

The mediator we mapped above might look like this:

@:keepSub
class UserProfileMediator extends Mediator
{
   @inject public var view:UserProfileView;

   public function new() {}

   override public function initialize():void
   {
      // initialize the view
      view.initialize();
   }
}

The view that caused this mediator to be created is available for Injection. MediatorMap

An Example Command

The command we mapped above might look like this:

@:keepSub
class UserSignInCommand extends Command
{
   @inject public var event:UserEvent;
   @inject public var model:UserModel;
   override public function execute():void
   {
      if (event.username == "bob")
         model.signedIn = true;
   }
}

The event that triggered this command is available for Injection. EventCommandMap