tag:robotlegs.tenderapp.com,2009-10-18:/discussions/problems/322-request-model-form-mediator-mechanismRobotlegs: Discussion 2018-10-18T16:35:27Ztag:robotlegs.tenderapp.com,2009-10-18:Comment/73806202011-05-22T11:34:31Z2011-05-22T11:34:33ZRequest model form mediator mechanism<div><p>Hi guys,</p>
<p>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.<br>
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 :</p>
<p>=> this is the login mediator, and he needs access to the the
"language setting". The mediator dispatches the setting request
event :</p>
<pre>
<code>dispatch(new SettingEvent( SettingEvent.RESUEST_SETTING, new SettingVO('language') ));</code>
</pre>
<p>=> the context notices the event and launches the
"RequestSettingCommand"</p>
<pre>
<code> commandMap.mapEvent(SettingEvent.REQUEST_SETTING, RequestSettingCommand, SettingEvent, false);</code>
</pre>
<p>=> The command gets the settingVO from the setting model and
dispatches the result settingVO back to the mediator(s)</p>
<pre>
<code> [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 );
}</code>
</pre>
<p>=> The login mediator listens to the SETTING_RESPONSE</p>
<pre>
<code> eventMap.mapListener(eventDispatcher, SettingEvent.SETTING_RESPONSE, setLogin );
private function setLogin(e:SettingEvent):void{
var language:String = e.settingVO.settingvalue;
}</code>
</pre>
<p>HERE IS THE PROBLEM:</p>
<p>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 :</p>
<pre>
<code> dispatch(new SettingEvent( SettingEvent.RESUEST_SETTING, {responsetarget:this , settingVO: new SettingVO('language')} ));</code>
</pre>
<p>and</p>
<pre>
<code> private function setLogin(event:SettingEvent):void{
if(event.responsetarget == this){
var language:String = event.settingVO.settingvalue;
}
}</code>
</pre>
<p>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?</p>
<p>Any suggestions would be very welcome :)</p>
<p>Thanks</p>
<p>Maarten</p></div>Maartentag:robotlegs.tenderapp.com,2009-10-18:Comment/73806202011-05-22T14:04:40Z2011-05-22T14:04:40ZRequest model form mediator mechanism<div><p>Hi Maarten,</p>
<p>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.</p>
<p>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?</p>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p>The simplest solution in this situation is to pass a callback to
be used to return the variable on the request event.</p>
<p>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.</p>
<p>I hope that helps,</p>
<p>Stray</p></div>Straytag:robotlegs.tenderapp.com,2009-10-18:Comment/73806202011-05-22T14:44:44Z2011-05-22T14:44:47ZRequest model form mediator mechanism<div><p>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...</p>
<p>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 :)</p>
<p>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 :)</p>
<p>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?</p>
<p>Thanks for helping ... I have some other questions, but I'll
start a new topic later on :)</p></div>Maartentag:robotlegs.tenderapp.com,2009-10-18:Comment/73806202011-05-22T15:03:04Z2011-05-22T15:03:04ZRequest model form mediator mechanism<div><p>Hi Maarten,</p>
<p>ah - yes, sounds like a model and not a config then :)</p>
<p>So - the code for what I was describing would look like
this:</p>
<pre>
<code>// 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);
}</code>
</pre>
<p>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.</p>
<p>Either approach is good I think,</p>
<p>Stray</p></div>Straytag:robotlegs.tenderapp.com,2009-10-18:Comment/73806202011-05-23T08:01:45Z2011-05-23T08:01:47ZRequest model form mediator mechanism<div><p>Thanks stray, great explanation.</p>
<p>I think you solution is better than mine. I'll do it by using a
callback function in the command.</p>
<p>Maarten</p></div>Maarten