Robotlegs Website Example Tutorial, Part II

by David Salahi on June 18, 2010

In the first part of this tutorial I discussed the process of wiring up views to mediators, injection of the model and the view and mapping an event to a command. In this part, I’m going to discuss the process of mapping view events to their mediators, the use of the Command object as a controller and outline the overall flow of events in this sample website app. See the first post to see the demo app running and to download the source code.

Mapping View Events to Their Mediators

The place to map a view’s events to its corresponding mediator is inside the mediator’s onRegister function which Robotlegs calls when the app is starting up. In this demo site, I have created a simple navigation menu inside a component called NavMenu.mxml. This component just contains a pair of buttons to allow the user to choose either the home page or the about page.

In this scenario, Robotlegs offers a couple of methods of mapping events. Here is one approach:

addViewListener(MouseEvent.CLICK, handleMouseClick);

With this statement (which could be added to the onRegister function), all mouse click events originating from the nav menu would be dispatched to the mediator. This would include clicks anywhere within the nav menu component, including clicks on the background as well as on the buttons. Furthermore, the dispatched events would not be conveniently traceable to their source. But the mediator needs to know which button was clicked so that it can communicate the desired web page to the Command object which will receive the event (or if no button was clicked to ignore the event). It would be possible to determine the button ID from the event target but this seemed like the wrong approach. The mediator shouldn’t need to know about the internals of the menu in order to do its job. This would also be error-prone since changing the ID of a button in the menu component would break code outside of the component that depends on it. 

For these reasons and because I didn’t want to worry about non-button mouse clicks I decided to take another approach. I decided to create a click listener for each button inside the nav menu and then to create a custom NavMenuEvent to send to the nav menu mediator. This NavMenuEvent contains a menuData property which can contain a constant corresponding to the desired web page (NavMenu.HOME_PAGE or NavMenu.ABOUT_US_PAGE).

So, instead of using addViewListener as mentioned above, I added conventional mouse click listeners for each of the two buttons inside the nav menu component:

protected  function homeBtn_clickHandler(event:MouseEvent):void
{
  var  menuEvent:NavMenuEvent = new NavMenuEvent(NavMenuEvent.MENU_EVENT);
  menuEvent.menuData  = HOME_PAGE;
  dispatchEvent(menuEvent);
}

Then, inside the click listeners I dispatched a NavMenuEvent containing the desired page constant. In order to receive this event in NavMenuMediator, I mapped a listener in the onRegister function:

eventMap.mapListener(_navMenu, NavMenuEvent.MENU_EVENT,  menuHandler);

So, whenever a menu button is clicked in NavMenu.mxml the menuHandler function in NavMenuMediator.as is called:

protected function menuHandler(event:NavMenuEvent):void
{
  var  pageChangeEvent:PageChangeEvent = new  PageChangeEvent(PageChangeEvent.PAGE_CHANGE_EVENT);
  pageChangeEvent.pageToChangeTo  = event.menuData;
  dispatch(pageChangeEvent);
}

menuHandler then dispatches a PageChangeEvent. Note the use of the Robotlegs Mediator dispatch function instead of the normal Flex dispatchEvent method. dispatch is a function provided by the Robotlegs  Mediator class which does not depend on the ActionScript display list.

As described in part I, the context registered the PageChangeCommand class as the handler for PageChangeEvents. So, whenever a PageChangeEvent occurs PageChangeCommand’s execute function is invoked. This controller then notifies the model that a new page has been requested and tells the view to change to that page:

override public function execute():void
{
  _websiteModel.changePage(event.pageToChangeTo);
  _websiteView.displayPage(event.pageToChangeTo);
}

Conclusion

To recap, the process starts in the WebsiteContext where most of the desired mappings occur. Additional mappings occur in the mediator onRegister functions at startup. Once the app is running a mouse click on one of the menu buttons triggers a mouse event that is handled inside NavMenu.mxml. Those button event listeners then create a NavMenuEvent which contains a string that specifies what the user’s desired page is. That event is received in NavMenuMediator which forwards a PageChangeEvent to PageChangeCommand. Finally, PageChangeCommand tells the model and the view how to handle the event.

In this two-part tutorial, I’ve covered many of the basic Robotlegs objects including events, mediators, commands, models, and views. Hopefully, by demonstrating the skeleton of a website you’ll be able to get similar projects up and running a bit quicker.
See part I of this post for the demo app and source code.

{ 4 comments… read them below or add one }

Nikos August 9, 2010 at 12:29 pm

very good, but I feel signals would be a easier solution to use.

Mark Barton August 10, 2010 at 3:15 pm

David – Good explanation.

Just started to look into Robotlegs and as many examples as possible help ;-)

Any reason why you used actionscript to add HomePage and AboutPage to the main view? Would it have mattered it you used MXML components?

One issue I need to solve is I want to put all of my service end points in a XML file which is then read in at run time. After following this discussion – http://knowledge.robotlegs.org/discussions/problems/131-accessing-stored-settings-in-model-through-out-application-best-practice Shaun recommends using the mapValue method in the context (rather than a god like object / model).

I assume I could do what you did with the WebsiteModel and read the XML file in first, create a model object and then configure the context by mapvalues from that model to each of the services?

Cheers

Mark

David Salahi August 10, 2010 at 4:48 pm

Hi Mark,
Thanks for the feedback.
Adding the Home and About pages with MXML would be the more common way to do it. The only reason I used ActionScript to add them is because it’s a more dynamic way of doing it. That is, with MXML the specific list of pages would be fixed at compile time. But with ActionScript the list of pages could be defined in an XML file, for example, and the desired number of pages could be created at run-time. Of course, this would mean you wouldn’t hard-code the page names as I did here. And you’d need additional parameters to define the specific features of each page.

Regarding your idea of putting your service endpoint info into an XML configuration file—that makes sense to me. Using the Robotlegs mapValue call would then be the logical way of injecting the data where needed. I used mapValue to map an already-created instance of the model rather than using mapSingleton and letting Robotlegs handle the instantiation. That’s because when I tried using mapSingleton I found that the sequence of events during object creation wasn’t convenient for my app.

One thing I’ve been learning as I’ve been working with Robotlegs is that you do have to be very aware of the life cycle of your objects. Robotlegs inserts itself into your startup code and at various places throughout your running app to instantiate objects upon demand. Robotlegs uses lazy instantiation and waits until objects are actually (about to be) used before creating them. This presumably enables quicker app startup and reduces resource usage. But it also means that sometimes objects aren’t ready when you expect them. (As an example, see my description of my August 6 bug fix for my SWFAddress sample.) So, you have to get familiar with where Robotlegs hooks into the object creation process and think about how that affects the order of creation of your obects.

Getting back to your question about service endpoints XML config files… in order to use mapValue you have to provide it with a specific instance of the object you want to map. To do that you’d just need getter functions in your model object to expose each of your various endpoint config objects.

Regards,
Dave

Mark Barton August 10, 2010 at 6:59 pm

Thanks for the info David.

I will go away and have a go – because I am getting the endpoints before the context has finished loading / mapping etc I assume I cannot use Robotlegs as the framework to get these endpoints – a bit of chicken & the egg.

Leave a Comment

 

{ 1 trackback }

Previous post:

Next post: