Handling Flex States & TitleWindow
Hi,
I have a popup window, when a button is clicked on it, its state changes, resulting in adding a new button to it. This button doesn't respond to clicking because when onRegister was called at the beginning, this button was null (because it is not added to stage yet, not until the state changes later on) and I couldn't add event handler to it. When states change, the onRegister doesn't get called again. So I am not sure how to handle this. Maybe I should listen for state change event, and then add the event listener over there (if the new button was already created by that time)... but not sure if this is the right way to handle this.
To make things more clear, I am creating a dialog (pop up window) that has a list of items. The user can select one of these items, and click the "details" button. Doing so, the state changes, the list disappears, a details form appears. The "details" button disappear, and a "back" button appears.
The onRegister has structure like the following:
override public function onRegister():void
{
// listen to view events
if( view.detailsButton )
eventMap.mapListener( view.detailsButton, MouseEvent.CLICK, detailsButton_clickHandler, MouseEvent );
if( view.backButton )
eventMap.mapListener( view.backButton, MouseEvent.CLICK, backButton_clickHandler, MouseEvent );
}
The onRegister function gets called only one time (when the window is popped up), at that time the state is "normal", the "back" button is not there yet, so the event listener is not added.
Sorry if this was asked elsewhere, but I have searched the forum for answers and I couldn't find my answer.
One last question, is there a good way to separate the component from the popup window. i.e., to fix the above problem, I thought about moving the whole code inside the pop up window into a normal component, then just use that component inside the popup window. However the problem with this method is that I am utilizing the "controlBar" of the TitleWindow, and I couldn't figure out a good way to have the buttons in the component appear in the controlBar of the TitleWindow.
I am feeling a bit sleepy, so I am sorry if I confused you. Please let me know if you need me to clarify anything that doesn't make sense.
Thanks,
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
Support Staff 1 Posted by Ondina D.F. on 05 Jul, 2012 10:15 AM
Hi!
A long-winded way of solving this would be:
In a UsersView I’d create 2 states: “list” and ”details”
then I would add
A UserListView and a button (id=”goToDetails) , both having includeIn="list".
A UserDetailsView and a button (id=”goToList) , both having includeIn="details".
I’d let UsersView change states in the handlers of the click event dispatched by goToDetails and goToList.
I’d mediate UserListView through UserListMediator and UserDetailsView through UserDetailsMediator.
UserListView would dispatch a custom event in the handler of IndexChangeEvent dispatched by the list.
UserListMediator would add an event listener for UsersEvent.USERS_LIST_SELECTED dispatched by the view:
addViewListener(UsersEvent.USERS_LIST_SELECTED, dispatch, UsersEvent);
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)
When UsersView changes states to “details”, the UserDetailsView gets added to the stage and its UserDetailsMediator gets created.
UserDetailsMediator in its onRegister() adds a listener:
addContextListener(UsersEvent.USERS_SHOW_DETAILS, onShowDetails, UsersEvent);
and dispatches an event to get the users data from the model:
dispatch(new UsersEvent(UsersEvent.USERS_GET_DETAILS));
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.
UserDetailsMediator in its onShowDetails:
view.showDetails(event.userData);
Done.
There are other solutions to get data for a view whose instantiation is deferred:
(Copied and pasted from another thread ;) http://knowledge.robotlegs.org/discussions/robotlegs-2/36-mediators... ):
Stray’s RelaxedEventMap (https://github.com/Stray/robotlegs-utilities-RelaxedEventMap) – probably the best solution
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())
the disputed ‘Inject your Model into your Mediator’ – with probably more nays than ayes . I’m abstaining ;)
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.
Please tell me which solution works best for you.
Cheers,
Ondina
Support Staff 2 Posted by Ondina D.F. on 05 Jul, 2012 10:25 AM
To add buttons to the control bar you only have to do this:
<s:controlBarContent>
</s:controlBarContent>
3 Posted by mbarjawi on 05 Jul, 2012 05:34 PM
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.
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:
UsersView
is aTitleWindow
popup.goToDetails
andgoToList
to appear on theUsersView
control bar (using thecontrolBarContent
as you mention).UserListView
andUserDetailsView
and placed them inside thecontrolBarContent
in theUsersView
.So things look something like this now:
list
state. So when theonRegister()
function (in theUsersViewMediator
class) is called, thegoToList
button is not created yet. Which means I couldn't not set event listener on thegoToList
button.details
, thegoToList
button is created, but theonRegister()
is not called anymore, and I couldn't set the listener on thegoToList
button.And this is exactly my problem:
1) I feel that the place of these buttons is better inside the
UserListView
andUserDetailsView
but I cannot keep them there as I wouldn't be able to put them in thecontrolBarContent
.2) I couldn't set the listener for the
goToList
button as it is not created yet when theonRegister()
is called.I hope things are more clear now. Thanks for your help.
4 Posted by mbarjawi on 06 Jul, 2012 02:57 AM
I think I found out one way of solving my problem. However, not sure if it is the best way out there.
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.
However, for some reason my mediators are not being attached/called... even though my code runs the:
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.
However, is the method mentioned in this post the right way to solve my problem?
5 Posted by mbarjawi on 06 Jul, 2012 03:36 AM
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.
However the question still stands, is this the best way to solve the problem at hand?
Thanks
Support Staff 6 Posted by Ondina D.F. on 06 Jul, 2012 08:02 AM
I’ll be back with answers in the course of the day. It’s 10 AM here:)
Support Staff 7 Posted by Ondina D.F. on 06 Jul, 2012 12:04 PM
In this post I’ll answer these:
>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.
>2) I couldn't set the listener for the goToList button as it is not created yet when the onRegister() is called.
In this scenario UsersView is not a TitleWindow. I’ll talk about popups in a following post.
If you want UsersMediator to react to state changes in the view (UsersView), you add an event listener like this in its onregister():
eventMap.mapListener(view, UsersEvent.USERS_STATE_CHANGED, onStateChanged, UsersEvent);
In UsersView you either dispatch that custom event in the handler of each button or you add an event listener for stateChangeComplete and
in stateChangeCompleteHandler you inform the mediator about the changes.
(Let me know if you don’t know how to create a custom event)
//sorry for the formatting!
<pre>
<code>
public function changeCurrentState(state:String):void
{
currentState=state;
}
protected function stateChangeCompleteHandler(event:FlexEvent):void
{
dispatchEvent(new UsersEvent(UsersEvent.USERS_STATE_CHANGED, currentState));
}
protected function onGoToList(event:MouseEvent):void
{
dispatchEvent(new UsersEvent(UsersEvent.USERS_STATE_CHANGED, "list"));
}
protected function onGoToDetails(event:MouseEvent):void
{
dispatchEvent(new UsersEvent(UsersEvent.USERS_STATE_CHANGED, "details"));
}
protected function goToDetails_clickHandler(event:MouseEvent):void
{
currentState="details";
//dispatchEvent(new UsersEvent(UsersEvent.USERS_STATE_CHANGED, currentState));
}
protected function goToList_clickHandler(event:MouseEvent):void
{
currentState="list";
//dispatchEvent(new UsersEvent(UsersEvent.USERS_STATE_CHANGED, currentState));
}
<s:states>
<s:State name="list"
<s:State name="details"
</s:states>
<s:controlBarContent>
<s:Button id="goToDetails" label="show details" includeIn="list" click="goToDetails_clickHandler(event)"
<s:Button id="goToList" label="show list" includeIn="details" click="goToList_clickHandler(event)"
</s:controlBarContent>
<components:UsersListView id="usersList" includeIn="list" x="10" y="10" itemDestructionPolicy="auto"
<components:UserDetailsView id="usersDetails" includeIn="details" x="10" y="10" itemDestructionPolicy="auto"
</code>
</pre>
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.
Why am I using a custom event?
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.
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.
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).
Why is not good to have goToDetails button in the UserDetails view and goToList button in your UsersListView?
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?
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.
The navigation between views is UsersView’s ( or any other parent view of UsersList and UsersDetails) responsibility.
To be continued..
Support Staff 8 Posted by Ondina D.F. on 06 Jul, 2012 01:38 PM
You’re right, you have to create/remove popup’s and its children mediators manually (in robotlegs 1).
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.
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.
In Robotlegs 2 the handling of popups is much easier than in Robotlegs 1, just for your information ;)
You asked:
and then
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…
One question from me:
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?
9 Posted by mbarjawi on 15 Jul, 2012 04:13 AM
Again thanks a lot for your efforts and replies. Sorry for taking so much long time to get back to you on this subject.
Answering your question:
In regards to my problem and its solution
view.importantAppSignal.add( myHandler );
<!-- UsersView Extends TitleWindow -->
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.
I hope this helps someone out there.
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.
Mutasem
Support Staff 10 Posted by Ondina D.F. on 16 Jul, 2012 08:23 AM
Glad to have been of help:)
Nice sum-up of our discussion!
mbarjawi closed this discussion on 24 Jul, 2012 12:57 AM.