tag:robotlegs.tenderapp.com,2009-10-18:/discussions/problems/614-handling-flex-states-titlewindowRobotlegs: Discussion 2012-07-24T00:57:44Ztag:robotlegs.tenderapp.com,2009-10-18:Comment/171075522012-07-05T10:15:26Z2012-07-05T10:15:26ZHandling Flex States & TitleWindow<div><p>Hi!</p>
<p>A long-winded way of solving this would be:</p>
<p>In a UsersView I’d create 2 states: “list” and
”details”</p>
<p>then I would add</p>
<p>A UserListView and a button (id=”goToDetails) , both
having includeIn="list".</p>
<p>A UserDetailsView and a button (id=”goToList) , both
having includeIn="details".</p>
<p>I’d let UsersView change states in the handlers of the
click event dispatched by goToDetails and goToList.</p>
<p>I’d mediate UserListView through UserListMediator and
UserDetailsView through UserDetailsMediator.</p>
<p>UserListView would dispatch a custom event in the handler of
IndexChangeEvent dispatched by the list.<br>
UserListMediator would add an event listener for
UsersEvent.USERS_LIST_SELECTED dispatched by the view:<br>
addViewListener(UsersEvent.USERS_LIST_SELECTED, dispatch,
UsersEvent);<br>
and immediately redispatch it triggering a command which would set
the selected user’s data on a UserModel. (or call a service
to retrieve data from an external resource)</p>
<p>When UsersView changes states to “details”, the
UserDetailsView gets added to the stage and its UserDetailsMediator
gets created.<br>
UserDetailsMediator in its onRegister() adds a listener:<br>
addContextListener(UsersEvent.USERS_SHOW_DETAILS, onShowDetails,
UsersEvent);<br>
and dispatches an event to get the users data from the model:<br>
dispatch(new UsersEvent(UsersEvent.USERS_GET_DETAILS));</p>
<p>which will trigger a command, which will read the
UserModel’s data and dispatch an
UsersEvent.USERS_SHOW_DETAILS with user’s data as a
payload.</p>
<p>UserDetailsMediator in its onShowDetails:<br>
view.showDetails(event.userData);</p>
<p>Done.</p>
<p>There are other solutions to get data for a view whose
instantiation is deferred:<br>
(Copied and pasted from another thread ;) <a href=
"http://knowledge.robotlegs.org/discussions/robotlegs-2/36-mediators-onregister-is-called-after-a-models-postconstruct-function-which-is-dispatching-an-event">
http://knowledge.robotlegs.org/discussions/robotlegs-2/36-mediators...</a>
):</p>
<ul>
<li>
<p>Stray’s RelaxedEventMap (<a href=
"https://github.com/Stray/robotlegs-utilities-RelaxedEventMap">https://github.com/Stray/robotlegs-utilities-RelaxedEventMap</a>)
– probably the best solution</p>
</li>
<li>
<p>ask for data within your onRegister() method: dispatch an event
triggering a command, that calls the Model (after registering an
event listener for the event dispatched by the Model in
Mediator’s onRegister())</p>
</li>
<li>
<p>the disputed ‘Inject your Model into your Mediator’
– with probably more nays than ayes . I’m abstaining
;)</p>
</li>
</ul>
<p>In any case I’d mediate UsersListView and UserDetailView,
and let UsersView handle the states changes. This way you could
re-use UsersListView and UserDetailView elsewhere, if need be.<br>
Please tell me which solution works best for you.<br>
Cheers,<br>
Ondina</p></div>Ondina D.F.tag:robotlegs.tenderapp.com,2009-10-18:Comment/171075522012-07-05T10:25:06Z2012-07-05T10:25:06ZHandling Flex States & TitleWindow<div><p>To add buttons to the control bar you only have to do this:<br>
<s:controlBarContent></p>
<pre>
<code> <s:Button id="someButton"/></code>
</pre>
<p></s:controlBarContent></p></div>Ondina D.F.tag:robotlegs.tenderapp.com,2009-10-18:Comment/171075522012-07-05T17:34:10Z2012-07-05T17:34:10ZHandling Flex States & TitleWindow<div><p>Ondina, thank you so much for your thoughts and efforts.
However, I am afraid you didn't answer my question, and I am
assuming because I wasn't so clear in asking it. Your efforts
didn't go in vain though... as you taught me about not injecting my
model into the mediator (which I am currently doing), I'll start
doing that after I fully understand the problem and its
solutions.</p>
<p>In my question above, I almost have the same design as what you
described (almost)... here is my problem using your example to make
things simpler:</p>
<ul>
<li><code>UsersView</code> is a <code>TitleWindow</code>
popup.</li>
<li>I want the two buttons <code>goToDetails</code> and
<code>goToList</code> to appear on the <code>UsersView</code>
control bar (using the <code>controlBarContent</code> as you
mention).</li>
<li>So I moved out those two buttons from inside the two views
<code>UserListView</code> and <code>UserDetailsView</code> and
placed them inside the <code>controlBarContent</code> in the
<code>UsersView</code>.</li>
</ul>
<p>So things look something like this now:</p>
<pre>
<code><UsersView> <!-- UsersView Extends TitleWindow -->
<states>
<State name="list"/>
<State name="details"/>
</states>
<UserListView includeIn="list"/>
<UserDetailsView includeIn="details"/>
<controlBarContent>
<Button id="goToDetails" includeIn="list" click="currentState='details'"/>
<Button id="goToList" includeIn="details" click="currentState='list'"/>
</controlBarContent>
</UsersView></code>
</pre>
<ul>
<li>Now, when the popup is first created and added to stage, it is
in the <code>list</code> state. So when the
<code>onRegister()</code> function (in the
<code>UsersViewMediator</code> class) is called, the
<code>goToList</code> button is not created yet. Which means I
couldn't not set event listener on the <code>goToList</code>
button.</li>
<li>When the state changes into <code>details</code>, the
<code>goToList</code> button is created, but the
<code>onRegister()</code> is not called anymore, and I couldn't set
the listener on the <code>goToList</code> button.</li>
<li>
<p>And this is exactly my problem:</p>
<p>1) I feel that the place of these buttons is better inside the
<code>UserListView</code> and <code>UserDetailsView</code> but I
cannot keep them there as I wouldn't be able to put them in the
<code>controlBarContent</code>.</p>
<p>2) I couldn't set the listener for the <code>goToList</code>
button as it is not created yet when the <code>onRegister()</code>
is called.</p>
</li>
</ul>
<p>I hope things are more clear now. Thanks for your help.</p></div>mbarjawitag:robotlegs.tenderapp.com,2009-10-18:Comment/171075522012-07-06T02:57:25Z2012-07-06T02:57:25ZHandling Flex States & TitleWindow<div><p>I think I found out one way of solving my problem. However, not
sure if it is the best way out there.</p>
<p>What I did is I created mediators for the two buttons
themselves, and then inside of each mediator, listened for the
MouseEvent.CLICK event, and dispatched an injected signal. The same
signal I am listening for inside the UsersView, which should solve
the problem.</p>
<p>However, for some reason my mediators are not being
attached/called... even though my code runs the:</p>
<pre>
<code>mediatorMap.mapView( goToList, goToListMediator );
mediatorMap.mapView( goToDetails, goToDetailsMediator );</code>
</pre>
<p>But still when the buttons are added to stage, the onRegister()
inside these two mediators are not being called. I guess I am
missing something over there. I'll take a second look and come back
with the results.</p>
<p>However, is the method mentioned in this post the right way to
solve my problem?</p></div>mbarjawitag:robotlegs.tenderapp.com,2009-10-18:Comment/171075522012-07-06T03:36:43Z2012-07-06T03:36:43ZHandling Flex States & TitleWindow<div><p>Just figured it out... it seems that not only the Popup window
that needs to have its mediator created/removed manually, but also
any component inside the popup window needs to have its mediator
manually created/removed. I did that for the two buttons and
bingo... it works.</p>
<p>However the question still stands, is this the best way to solve
the problem at hand?</p>
<p>Thanks</p></div>mbarjawitag:robotlegs.tenderapp.com,2009-10-18:Comment/171075522012-07-06T08:02:03Z2012-07-06T08:02:03ZHandling Flex States & TitleWindow<div><p>I’ll be back with answers in the course of the day.
It’s 10 AM here:)</p></div>Ondina D.F.tag:robotlegs.tenderapp.com,2009-10-18:Comment/171075522012-07-06T12:04:36Z2012-07-06T12:08:37ZHandling Flex States & TitleWindow<div><p>In this post I’ll answer these:<br>
>1) I feel that the place of these buttons is better inside the
UserListView and UserDetailsView but I cannot keep them there as I
wouldn't be able to put them in the controlBarContent.</p>
<p>>2) I couldn't set the listener for the goToList button as it
is not created yet when the onRegister() is called.</p>
<p>In this scenario UsersView is not a TitleWindow. I’ll talk
about popups in a following post.<br>
If you want UsersMediator to react to state changes in the view
(UsersView), you add an event listener like this in its
onregister():</p>
<p>eventMap.mapListener(view, UsersEvent.USERS_STATE_CHANGED,
onStateChanged, UsersEvent);</p>
<p>In UsersView you either dispatch that custom event in the
handler of each button or you add an event listener for
stateChangeComplete and<br>
in stateChangeCompleteHandler you inform the mediator about the
changes.<br>
(Let me know if you don’t know how to create a custom
event)</p>
<p>//sorry for the formatting!</p>
<p><pre><br>
<code><br>
public function changeCurrentState(state:String):void<br>
{<br>
currentState=state;<br>
}</p>
<p>protected function
stateChangeCompleteHandler(event:FlexEvent):void<br>
{<br>
dispatchEvent(new UsersEvent(UsersEvent.USERS_STATE_CHANGED,
currentState)); <br>
}</p>
<p>protected function onGoToList(event:MouseEvent):void<br>
{<br>
dispatchEvent(new UsersEvent(UsersEvent.USERS_STATE_CHANGED,
"list")); <br>
}<br>
<br>
protected function onGoToDetails(event:MouseEvent):void<br>
{<br>
dispatchEvent(new UsersEvent(UsersEvent.USERS_STATE_CHANGED,
"details")); <br>
}<br>
<br>
protected function
goToDetails_clickHandler(event:MouseEvent):void<br>
{<br>
currentState="details";<br>
//dispatchEvent(new UsersEvent(UsersEvent.USERS_STATE_CHANGED,
currentState));
<br>
}<br>
<br>
protected function goToList_clickHandler(event:MouseEvent):void<br>
{<br>
currentState="list";<br>
//dispatchEvent(new UsersEvent(UsersEvent.USERS_STATE_CHANGED,
currentState));<br>
}<br>
<s:states><br>
<s:State name="list"<br>
<s:State name="details"<br>
</s:states><br>
<s:controlBarContent><br>
<s:Button id="goToDetails" label="show details" includeIn="list"
click="goToDetails_clickHandler(event)"<br>
<s:Button id="goToList" label="show list" includeIn="details"
click="goToList_clickHandler(event)"<br>
</s:controlBarContent> <br>
<components:UsersListView id="usersList" includeIn="list" x="10"
y="10"
itemDestructionPolicy="auto" <br>
<components:UserDetailsView id="usersDetails"
includeIn="details" x="10" y="10"
itemDestructionPolicy="auto" <br>
</code><br>
</pre></p>
<p>If UsersView wouldn’t be a TitleWindow, the mediators
(UsersMediator, UsersListMediator, UserDetailsMediator) would be
created automatically in response to their views being added to the
display list.</p>
<p>Why am I using a custom event?<br>
The mediator shouldn’t care about the details of the view. If
I changed my mind and wanted the view to change the currentState in
response to another user’s interaction (selectedItem in a
list or dataGrid, or a mouse over etc) I would have to add event
listeners in the mediator for all these occurrences. Or, in your
scenario, if I’d wanted to add another state to the stack,
say “images”, and then another state
“sounds”, I’d have to add new buttons and
of course new event listeners in my mediator.</p>
<p>As you saw, you couldn’t add an event listener in your
mediator to a component that wasn’t on stage at the time
onRegister() has run. With a custom event you wouldn’t have
this problem.</p>
<p>With a custom event things are more flexible, reusable and
decoupled, and, also, the event listeners in the mediator
wouldn’t create a strong reference to the view and/or its
subcomponents (important for gc).</p>
<p>Why is not good to have goToDetails button in the UserDetails
view and goToList button in your UsersListView?<br>
Because UsersListView and UsersDetailsView shouldn’t care
bout their parent’s state and if you want to re-use them
elsewhere, where state changes aren’t required, what are you
going to do? Comment out the buttons?<br>
And if you had more than 2 states in the parent view, would you add
a goToImages and a goToSounds buttons in your UserListView to
satisfy the new requirements? UsersListView’s single
responsibility is to present a list of users and to inform its
mediator about selected items.</p>
<p>The navigation between views is UsersView’s ( or any other
parent view of UsersList and UsersDetails) responsibility.</p>
<p>To be continued..</p></div>Ondina D.F.tag:robotlegs.tenderapp.com,2009-10-18:Comment/171075522012-07-06T13:38:40Z2012-07-06T13:38:40ZHandling Flex States & TitleWindow<div><blockquote>
<p>Just figured it out... it seems that not only the Popup window
that needs to have its mediator created/removed manually, but also
any component inside the popup window needs to have its mediator
manually created/removed. I did that for the two buttons and
bingo... it works.</p>
</blockquote>
<p>You’re right, you have to create/remove popup’s and
its children mediators manually (in robotlegs 1).<br>
You can let UsersView create the mediators for UsersListView and
UserDetailsView in its onStateChanged method, called after the view
dispatches the UsersEvent.USERS_STATE_CHANGED from the example in
the previous post, or trigger a command that would do just
that.</p>
<p>To be able to add a mediator for UsersListView, which is
included in the “list” state, the first one when
UsersView gets created, you’ll probably have to introduce a
new “start” state as the initial state and in your
UsersMediator onRegister() change the UsersView state to
“list”, in case you want to react to state changes.</p>
<p>In Robotlegs 2 the handling of popups is much easier than in
Robotlegs 1, just for your information ;)</p>
<p>You asked:</p>
<blockquote>
<p>However, is the method mentioned in this post the right way to
solve my problem?</p>
</blockquote>
<p>and then</p>
<blockquote>
<p>However the question still stands, is this the best way to solve
the problem at hand?</p>
</blockquote>
<p>I don’t know anymore what method or way are you referring
to, there where several aspects to your use case: popups, states,
where to put the buttons, how to add event listeners…</p>
<p>One question from me:<br>
Do you need to use a popup in this way, meaning should the list
view and the details view be contained in the popup? Or you rather
meant to open a popup with details for an item selected from a
list?</p></div>Ondina D.F.tag:robotlegs.tenderapp.com,2009-10-18:Comment/171075522012-07-15T04:13:00Z2012-07-15T04:13:00ZHandling Flex States & TitleWindow<div><p>Again thanks a lot for your efforts and replies. Sorry for
taking so much long time to get back to you on this subject.</p>
<ul>
<li>
<p>Answering your question:</p>
<blockquote>
<p>Do you need to use a popup in this way, meaning should the list
view and the details view be contained in the popup? Or you rather
meant to open a popup with details for an item selected from a
list?</p>
</blockquote>
<ul>
<li>Well, my software has area where a study plan can be created
for students. Now, in that area, I want the user to be able to
click an "Add" button to add another item to the study plan. Once
the "add" button is clicked, a dialog pops up showing the list of
items from which the user can choose. This list, doesn't contain
details of each items. So in the dialog itself, I added a "details"
button, when clicked, the current selected study plan item will be
viewed in details by switching the state of the dialog into the
"details" view. I thought this is better than opening another
dialog on top of the first dialog.</li>
</ul>
</li>
<li>
<p>In regards to my problem and its solution</p>
<ul>
<li>My solution was a combination based on your post (post 8) and
on my post (post 5). You inspired me a lot in post 8... so thanks a
lot.</li>
<li>What I did was to separate all view specific events/actions
what so ever... from the rest of the application (which is
connected through the mediator).</li>
<li>For example, in the mediator, I think we should not listen for
a MouseEvent.CLICK event coming from one of the buttons... because
this will make the mediator too much tightly coupled with the
view.</li>
<li>Instead, in the mediator we should listen for application
specific events coming from the view. For example, the view itself
listens for its CLICK event, then dispatches a
"IMPORTANT_APP_EVENT" that the mediator is listening for.</li>
<li>I found it event simple and easier to create signals on the
view itself. So in the mediator, it would look something like:
<code>view.importantAppSignal.add( myHandler );</code></li>
<li>This helped me solve my problem in the following way: 1- In the
dialog, I listened events coming from the buttons. Based on the
examples we discussed above, the view would look something like
that:</li>
</ul>
<p><!-- UsersView Extends TitleWindow --></p>
<pre>
<code> <states>
<State name="list"/>
<State name="details"/>
</states>
<script>
public var importantAppSignal:Signal;
protected function goToDetailsHandler():void
{
currentState='details';
}
protected function goToDetailsHandler():void
{
currentState='list';
importantAppSignal.dispatch();
}
</script>
<UserListView includeIn="list"/>
<UserDetailsView includeIn="details"/>
<controlBarContent>
<Button id="goToDetails" includeIn="list" click="goToDetailsHandler()"/>
<Button id="goToList" includeIn="details" click="goToListHandler()"/>
</controlBarContent></code>
</pre>
<p>2- This way, even if the button is added later on in the life of
the dialog, the handler will still be called. The outside world
doesn't need to know this much specific details about the view.
This is why all this happens inside the view. The mediator only
cares about the signal that got dispatched in these handlers.</p>
</li>
</ul>
<p>I hope this helps someone out there.<br>
Thanks Ondina a lot for your support, I will wait couple of days
before closing this thread, in case anybody had some comments to
add.</p>
<p>Mutasem</p></div>mbarjawitag:robotlegs.tenderapp.com,2009-10-18:Comment/171075522012-07-16T08:23:35Z2012-07-16T08:29:17ZHandling Flex States & TitleWindow<div><p>Glad to have been of help:)<br>
Nice sum-up of our discussion!</p></div>Ondina D.F.