Request model form mediator mechanism

Maarten's Avatar

Maarten

22 May, 2011 11:34 AM

Hi guys,

I'm new to robotlegs and doing my first application in adobe air. The problem I'm facing is about avoiding model injection in mediators.
In my app, I have a settings model, which stores global application setting data. I need this data often in different mediators, but I understand that it's better not to inject the setting model in all those mediators. Instead, I should use a command to request a setting VO, and send it back to the mediator. I'm doing something like this code below :

=> this is the login mediator, and he needs access to the the "language setting". The mediator dispatches the setting request event :

dispatch(new SettingEvent(  SettingEvent.RESUEST_SETTING, new SettingVO('language')  ));

=> the context notices the event and launches the "RequestSettingCommand"

    commandMap.mapEvent(SettingEvent.REQUEST_SETTING, RequestSettingCommand, SettingEvent, false);

=> The command gets the settingVO from the setting model and dispatches the result settingVO back to the mediator(s)

    [Inject]
    public var settingmodel:SettingModel;

    override public function execute():void{
                    var requestedsetting:SettingVO = settingmodel.getSetting(event.settingVO.settingname);
        dispatch(new SettingEvent(SettingEvent.SETTING_RESPONSE, requestedsetting );
    }

=> The login mediator listens to the SETTING_RESPONSE

    eventMap.mapListener(eventDispatcher, SettingEvent.SETTING_RESPONSE, setLogin );

            private function setLogin(e:SettingEvent):void{
                   var language:String = e.settingVO.settingvalue;
            }

HERE IS THE PROBLEM:

If I use this mechanism across multiple mediators, each mediator will always listen to the same SETTING_RESPONSE event. This is not what I want, because the SETTING_RESPONSE should only be targeted to the mediator that did the SETTING_REQUEST. I was thinking about adding a responsetarget parameter to the SettingEvent itself. So in the mediator, I would do something like this :

         dispatch(new SettingEvent(  SettingEvent.RESUEST_SETTING, {responsetarget:this , settingVO: new SettingVO('language')}  ));

and

            private function setLogin(event:SettingEvent):void{
                   if(event.responsetarget == this){
                          var language:String = event.settingVO.settingvalue;
                   }
            }

Although it prevents coupling, I'm not sure if this is the right way of doing it. It doesn't feel right. Am I doing it totally wrong? Or is this a valid strategy? How could I improve this mechanism?

Any suggestions would be very welcome :)

Thanks

Maarten

  1. Support Staff 1 Posted by Stray on 22 May, 2011 02:04 PM

    Stray's Avatar

    Hi Maarten,

    The 'don't inject models and services into your mediators' advice is based on a few things, and I'm not sure your situation matches that.

    I don't think your 'model' is a model. It's really more like a configuration - yes? As in, its state doesn't change after run time, and it has no writable-api?

    The reason we advise avoiding injecting models into mediators is about avoiding ending up with complex logic in your mediator - there are numerous reasons why that can end up getting messy very fast, to do with race conditions, the fact that the mediator isn't stateful (each instance is destroyed and a new one created when a view comes and goes from the stage) and you can wind up with a lot of duplication.

    In addition, mediator injections are prone to suffering from race conditions - if your view hits the stage before your config has loaded then the injection can't be fulfilled yet and you'll get a null error if it hasn't been mapped, or null values if the data in that model isn't yet populated. Yuk.

    So - if your settings VO isn't really stateful - just a config - and if you're certain it will be available before these mediators are created - I think it's reasonable to inject it - though still not ideal. If you do want to inject it and it has a writable-api then I would inject it against a read-only API.

    The other option is to use a request/response pattern. As you say - the trouble with request/response events is that you wind up with every listener responding to every response.

    The simplest solution in this situation is to pass a callback to be used to return the variable on the request event.

    Alternatively you can do it with AS3 signals, which gives you more intelligent error checking (at runtime). If you're familiar with signals and you want to do it this way, shout and I'll give you a code example. If you're not familiar with signals then go with injection or passing a callback on your requestEvent.

    I hope that helps,

    Stray

  2. 2 Posted by Maarten on 22 May, 2011 02:44 PM

    Maarten's Avatar

    Thanks Stray, I understand what you are telling me. The thing is that the settings model is statefull. I have a setting view, where the user can configure program settings...

    So I guess it would be cleaner if I go with the request/response pattern for the settings. The fact that every listener will catch the events, is a problem. I'm just learning robotlegs, so for this project I'm not yet going to dig into the signals thing... I put it on my todo list :)

    But what do you exactly mean bij passing a callback to the request event? Passing a reference to a callbackfunction on the mediator? That sounds odd :)

    One more thing. I use the application model (model of contextview) to store those application wide config settings. Examples: currentlanguage, currentpage , pagehistory, firstrun, currentversion . I guess this a valid strategy?

    Thanks for helping ... I have some other questions, but I'll start a new topic later on :)

  3. Support Staff 3 Posted by Stray on 22 May, 2011 03:03 PM

    Stray's Avatar

    Hi Maarten,

    ah - yes, sounds like a model and not a config then :)

    So - the code for what I was describing would look like this:

    // the request event takes a handler as its payload
    public function RequestSettingsEvent(eventType:String, responseHandler:Function, etc...)
    
    // the mediator dispatches its local handler as this payload
    public function onRegister():void
    {
        dispatch(new RequestSettingsEvent(RequestSettingsEvent.SETTINGS_REQUESTED, receiveSettings));
        addContextListener(SettingsChangeEvent(SETTINGS_CHANGED, settingsChangedHandler));
    }                                                                                     
    
    protected function settingsChangedHandler(e:SettingsChangeEvent):void
    {
        receiveSettings(e.vo);
    }           
    
    protected function receiveSettings(settingsVO:ISettingsVO):void
    {
        // do whatever is needed with the settings
    }                                             
    
    // the command that handles the request              
    
    [Inject]
    public var requestEvent:RequestSettingsEvent;
    
    [Inject]
    public var settingsModel:ISettings;
    
    public function execute():void
    {
        var vo:ISettingsVO = settingsModel.getVO();
        requestEvent.responseHandler(vo);
    }
    

    Anyway - the way you have suggested will work too - really they are the same thing, it's only that this approach is less work at runtime (for the program) because you're not having a bunch of mediators run their handler and then execute.

    Either approach is good I think,

    Stray

  4. 4 Posted by Maarten on 23 May, 2011 08:01 AM

    Maarten's Avatar

    Thanks stray, great explanation.

    I think you solution is better than mine. I'll do it by using a callback function in the command.

    Maarten

  5. Stray closed this discussion on 25 May, 2011 07:57 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