RL2 Bootstrap command chain

iamable's Avatar

iamable

24 Oct, 2014 07:52 AM

Hey,

I am trying to implement a bootstrap command chain for RL2.
I read thiis post http://knowledge.robotlegs.org/discussions/robotlegs-2/10314-chaini...
but now I got stucked.

The main class constructor looks like this:
_context = new Context()
.install(MVCSBundle) .configure(AppConfig) .configure(new ContextView(this));

I dont know where to trigger the command chain.
Is it right to do thiy within AppConfig?
commandMap.map(ContextEvent.STARTUP, ContextEvent).toCommand(BootstrapApplicationCommand).once();

        eventDispatcher.dispatchEvent(new ContextEvent(ContextEvent.STARTUP));

In RL1 there was a ContextEvent. In RL2 there isn't anymore.
What to use instead?

Thank you

  1. Support Staff 1 Posted by Ondina D.F. on 24 Oct, 2014 11:41 AM

    Ondina D.F.'s Avatar

    First of all, let's clarify what should happen in your BootstrapApplicationCommand.

    If by bootstrapping you mean just the mappings of Models, Mediators, Services and Commands, the easiest way to do that is to have config classes for each category of mappings and tell the Context to use them for configuration.

    For example a MediatorsConfig would look like this:

    public class MediatorsConfig implements IConfig
    {
        [Inject]
        public var mediatorMap:IMediatorMap;
    
        public function configure():void
        {
            mediatorMap
                .map(SomeView)
                .toMediator(SomeMediator);
        }
    }
    

    Note that MediatorsConfig implements IConfig and has a configure() method which will be called automatically by the context. You put your mappings inside the configure().

    A ControllersConfig could look like this:

    public class ControllersConfig implements IConfig
    {
        [Inject]
        public var commandMap:IEventCommandMap;
        
        public function configure():void
        {
            commandMap
                .map(SomeEvent.SOME_TYPE, SomeEvent)
                .toCommand(SomeCommand);
        }
    }
    

    When you create and configure your context, you let the context use your custom configurations:

    _contextView = new ContextView(view);
    _context = new Context();
    _context.install(MVCSBundle);
    
    //add your config classes in the needed order:
    _context.configure(ModelsConfig, MediatorsConfig, ServicesConfig, ControllersConfig);
    
    _context.configure(_contextView);
    

    That is all you need for a simple configuration. Say more about your workflow, in case that kind of configuration is not sufficient for you.

    In RL1 there was a ContextEvent. In RL2 there isn't anymore.

    The robotlegs 2 Context has a lifetime, aka lifecycle: it is created, used, then destroyed (or not).
    The rl2's Context creates a LifeCycle for itself (https://github.com/robotlegs/robotlegs-framework/blob/master/src/ro... ) and raises a number of events (LifecycleEvent) that you can hook on to.
    https://github.com/robotlegs/robotlegs-framework/blob/master/src/ro...

    Context's lifecycle hooks are:

    beforeInitializing
    whenIntitializing
    afterIntializing
    
    beforeSuspending
    whenSuspending
    afterSuspending
    
    beforeResuming
    whenResuming
    afterResuming
    
    beforeDestroying
    whenDestroying
    afterDestroying
    

    afterIntializing is a very useful hook, when you want to make sure that something happens after the Context has been fully initialized and all mappings created. That way you can avoid running into race conditions. To use it, you just create a handler like this:

    _context = new Context();
    
    // A handler to run after initialization:::
    _context.afterInitializing(yourAfterInitializingHandler);
    
    _context.install(MVCSBundle);
    _context.configure(ModelsConfig, MediatorsConfig, ServicesConfig, ControllersConfig);
    _context.configure(_contextView);
    
    private function yourAfterInitializingHandler ():void
    {
        //do something
    }
    

    If you want to trigger a Command from within one of your config classes, you can do it like in your example, you inject a shared event dispatcher into your config class, you map a custom event to a command inside the configure() method and then dispatch the event on the shared event dispatcher. The difference is that you use a custom event instead of context's event.

  2. 2 Posted by iamable on 24 Oct, 2014 12:30 PM

    iamable's Avatar

    Thanks Ondina,

    the flow of configuring the mappings as single configs is great.
    _context.configure(ModelsConfig, MediatorsConfig, ServicesConfig, ControllersConfig);
    are them configured one after the other?

    What could be a further step for bootstrapping?

    I have anothzer question. Is there a good tutorial explaining an example like this?
    https://github.com/robotlegs/robotlegs-demo-TodoList
    I need someone/something explaining how to use signals, behaviors and actions. Most people are developing in the way from that example and i would like to learn it but I dont know here to start. I dont really know why and how to seperate behaviors and link the to different interfaces related to the view. Until now I just have several views and a mediator of it. but this seems not to be the best way. but for understanding why I need some detailed explanations.

    I hope you undertsand what I mean

  3. Support Staff 3 Posted by Ondina D.F. on 24 Oct, 2014 04:09 PM

    Ondina D.F.'s Avatar

    You're welcome :)

    are them configured one after the other?

    Yes.

    What could be a further step for bootstrapping?

    Hmm. I don't know. It depends on your needs. What else do you need to do there?

    I have anothzer question.

    It is always better to open a new discussion, if the question has nothing to do with the current discussion :)

    Is there a good tutorial explaining an example like this?

    I don't know if the following links are what you need. Anyway here they are:

    http://justinjmoses.wordpress.com/2011/08/07/ui-mediation-sucks-med...

    http://justinjmoses.wordpress.com/2011/07/07/whats-the-deal-with-si...

    I need someone/something explaining how to use signals, behaviors and actions.

    I can understand very well your need for guidance.
    I would like to help you, but it takes a lot of time to explain everything in detail, and sadly I don't have that much time. I think that you need to understand the basic principles of rl and MVCS and how everything works using events first. Have you read the Best Practices yet? And Joel's tutorials? Or, better, the rl book? They are for rl1, but still a good start in understanding rl. Do you need links to the tutorials / book?

    I can't promise anything (!!), but if I get a chance next week, I'll try to explain how to use interfaces to mediate behaviours. You should do some reading (best practices, rl book) in the meantime ;)

  4. Support Staff 4 Posted by Ondina D.F. on 28 Oct, 2014 02:43 PM

    Ondina D.F.'s Avatar

    I've put together an example (AIR 3.9, FlashBuilder 4.6) this morning. See attachment.

    There is a MorphogenView mediated by MorphogenMediator. I named it Morphogen, because it controls the type and position of shapes in different sub-views.

    MorphogenView holds

    • 3 sub-views:

      • MonomorphicViewOne
      • MonomorphicViewTwo
      • PolymorphicView
    • a set of buttons to control the creation, deletion and rotation of shapes in the 3 sub-views

    • and a TextArea for logging

    Monomorphic - because it creates only one shape
    Polymorphic - because it creates 2 (or more) shapes.

    The 3 sub-views extend a BaseShapeView. Each of the 3 sub-views implements a different set of interfaces.

    The interfaces are:

    • IMakeCircles

    • IMakeSquares

    • IRotateShapes

    • IResetShapes

    As their names suggest, they offer an API for different functionalities, that are the methods of BaseShapeView.

    • MonomorphicViewOne implements IMakeSquares, IRotateShapes, IResetShapes

    • MonomorphicViewTwo implements IMakeCircles, IRotateShapes, IResetShapes

    • PolymorphicView implements IMakeSquares, IMakeCircles, IRotateShapes, IResetShapes

    Because of the interfaces that are implemented, each view exhibits a different/specific or a shared behaviour

    • MonomorphicViewOne's special behaviour is to make squares

    • MonomorphicViewTwo's special behaviour is to make circles

    • PolymorphicView's behaviour is to make squares and circles

    All 3 views are capable to rotate themselves and to delete the contained shapes, because they all implement those behaviours (IRotateShapes, IResetShapes)

    From within the click event handlers of the buttons in MorphogenView a custom event, that I named BehavioursEvent, will be dispatched.
    Say, the "make circles" button is clicked. This will be translated into a request, BehavioursEvent.MAKE_CIRCLE_REQUESTED.
    MorphogenMediator will re-dispatch the request (event).

    The sub-views should be able to react to that request and to do their thing (apply the corresponding behaviour == draw circles). In fact, only the views capable of making circles should react to the event. To achieve this, a MakeCirclesMediator, injected with an IMakeCircles, will let the views that implement IMakeCircles call makeCircle() method. As a consequence, only MonomorphicViewTwo and PolymorphicView will draw a circle. MonomorphicViewOne won't be bothered by the request.

    If MorphogenView would request squares (BehavioursEvent.MAKE_SQUARE_REQUESTED), only MonomorphicViewOne and PolymorphicView would react, because their behaviour is mediated by MakeSquaresMediator, which is injected with IMakeSquares.

    When MorphogenView sends a ROTATE_SHAPE_REQUESTED event, all views will react to it, because the behaviour is mediated by RotateShapesMediator, which is injected with an IRotateShapes.

    PolymorphicView has also a Mediator for itself, PolymorphicMediator. I only created it to show you that it is possible to have it next to the "behavioural" mediators. At the moment only PolymorphicMediator is listening for LoggerEvent.

    It might sound complicated, but it is not. You should run the example in order to better understand the explanations.

    I intentionally created 2 separates behaviours, like IMakeCircles and IMakeSquares, just for the sake of an example. In a real application you would want to abstract those behaviours even further. My example shows only one of the many possible use cases of mediating behaviours instead of concrete views.

    So, a behaviour can be a certain functionality or a set of functionalities, an action or a reaction, that a component may display.

    I hope that helps.

  5. Ondina D.F. closed this discussion on 05 Dec, 2014 11:52 AM.

  6. iamable re-opened this discussion on 21 Apr, 2015 01:01 PM

  7. 5 Posted by iamable on 21 Apr, 2015 01:01 PM

    iamable's Avatar

    Hey Ondina,

    sorry for that late reply. I wasn't programming for almost half a year.
    I had some time now to have a look at your example project.
    First of all thank you very much for that effort.
    Now I understand why and how to mediate against behaviors.
    If I understand right: Someone is dispatching an event for a specific behavior. The Behavior mediator is listening for it. he has the injected interface for the behavior calling a method. the view implements that interface and its behavior. so the mediator and the view is decoupled. Am I right?

    What I am missing in your project is the handling of application data.
    How to handle models in RL2. In the past I had an actor dispatching an event. the listening mediator injects the model and changes the view.
    I've just read injecting the model into the mediator is not the best way.
    Do you have any best practice how to handle changed model data and how to change the view based on that data?

  8. 6 Posted by iamable on 21 Apr, 2015 02:13 PM

    iamable's Avatar

    I also have problems in dispatching events from the model. Injecting IEventDispatcher doesnt work. The instance is always null

  9. 7 Posted by iamable on 21 Apr, 2015 02:37 PM

    iamable's Avatar

    OK second problem is solved by letting the injector instatiate the model

    var model:Model = injector.instantiateUnmapped(Model);
    injector.map(Model).toValue(model);

  10. Support Staff 8 Posted by Ondina D.F. on 22 Apr, 2015 03:56 PM

    Ondina D.F.'s Avatar

    Hi,

    You're welcome.

    How is life without programming? ;)

    I've modified the previous example in order to show you how to dispatch events from a model.

    I've added a StatisticsView coupled with a StatisticsMediator.
    When a shape is added or removed from the views, a StatisticsEvent is dispatched (from within the BaseShapeView) with a payload containing a ShapeVO. The ShapeVO has properties like shapeId, shapeSize, shapeColor...
    MakeCirclesMediator and MakeSquaresMediator are listening to the events dispatched by the views, and are redispatching them in order to trigger commands.

    The commands, AddElementCommand and RemoveElementCommand are mapped to the StatisticsEvent .ELEMENT_ADDED and respectiveley to StatisticsEvent . ELEMENT_REMOVED. They are accessing the StatisticsModel's methods addElement() and removeElement(). The StatisticsModel does a rudimentary math operation and then it dispatches a StatisticsEvent.STATISTICS_RESULTS with a payload.

    The StatistcsMediator, which is listening to this event, passes the payload of the event on to the StatisticsView, which is populating a datagrid and 2 input fields with the results. The datagrid is showing the ids, names and colors of the shapes, and the 2 input fields the math results.

    For example:

    1. BaseShapeView.makeShape-> dispatchEvent(new StatisticsEvent(StatisticsEvent.ELEMENT_ADDED, shapeVO));

    2. MakeCirclesMediator.onElementAdded -> dispatch(event);

    3. AddElementCommand -> statisticsModel.addElement(event.payload as ShapeVO);

    4. StatisticsModel -> dispatcher.dispatchEvent(new StatisticsEvent(StatisticsEvent.STATISTICS_RESULTS, doSomeMath()));

    5. StatisticsMediator.onStatsticsResults(event:StatisticsEvent)- > view.onStatisticsResults(event.payload);

    6. StatisticsView->onStatisticsResults -> statisticsGrid.dataProvider = payload["collection"];

    I hope my explanation is clear enough. It will be easier to understand, if you run the application.

    Ondina

  11. 9 Posted by iamable on 23 Apr, 2015 03:11 PM

    iamable's Avatar

    Thanks Ondina

    I understand that but why does StatistcsMediator not relates to an interface?
    As I learned it is better to mediate behavior instead of views.

  12. Support Staff 10 Posted by Ondina D.F. on 24 Apr, 2015 12:30 PM

    Ondina D.F.'s Avatar

    I understand that but why does StatistcsMediator not relates to an interface?

    Because I used it to answer your question about how to dispatch events from models. It was just an example, and there was no need for an interface.

    As I learned it is better to mediate behavior instead of views.

    There is no better or worse, it all depends on what you need to achieve. In other words, it depends on the nature and overall design of your application.

    Of course interfaces are cool, but using interfaces does not necessarily and automatically guarantee high quality of the code. There are good APIs and there are bad APIs. Just saying;)

    When you are mediating interfaces, you have to take into account the way mediators react to events. If several views implement the same interface, every time an event has been dispatched, the mediator will react to it. So, for example, several views implement an interface ICleanUp with a method cleanUp(), which resets the input fields in the views.
    An event dispatched by a deleteButton is listen to by a CleanUpMediator, which then lets the view call its cleanUp() method. As a consequence, every single view in your application that implements ICleanUp will delete the contents of their input fields. That's handy and that's probably exactly what you're after. But, maybe there are other cases where you wouldn't want a certain view to always react to a general clean up request...

    Other things to consider
    A view may need to exhibit a certain behaviour, thus it implements the corresponding interface. But, the view will need to implement all of the methods declared in that interface, even if it needs just one of them.
    A view may also end up implementing lots of interfaces. Depending on how big and complex your application will be, or on how you design it, or on the level of granularity of your views, there might be lots and lots of interfaces representing behaviours. You'll have to ponder whether it is a desired effect to have lots of mediators for lots of interfaces, or not.

    All I'm saying with this is that if you decide that your application would benefit from mediating behaviours in form of interfaces, you need to know what to expect from such an approach. You should try to find the design pattern or approach that will fit your application's needs, not vice versa.

    I hope everything is clear now, and you can start experimenting with mediation of behaviours :)

  13. Ondina D.F. closed this discussion on 11 May, 2015 11:31 AM.

Comments are currently closed for this discussion. You can start a new one.

Keyboard shortcuts

Generic

? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac