When building the events scenario for the Facebook application I saw a screen that looks like this:

Two lists. One shows the events coming up, and the other birthdays forthcoming. As a Web developer I looked at this in a simple way. Literally two lists. When you click on a different command menu button, you show one list and hide the other.
So, off I go to the races, and I will now walk through the code required to do this.
Initialization and Setup
var EventsListAssistant = Class.create(NavbarAssistant, {
initialize: function($super) {
$super();
this.eventList = new EventList();
this.birthdayList = new BirthdayList();
this.gotoEventViewHandler = this.gotoEventView.bind(this);
this.gotoProfileViewHandler = this.gotoProfileView.bind(this);
},
setup: function($super) {
$super();
this.setupCommandMenu();
this.setupEventsList();
this.setupBirthdaysList();
this.showEventsList();
},
First we create an assistant for this scenario, and it inherits from the Navbar giving it the Facebook top menu bar.
Then we wire up the data sources for both events (eventList) and birthdays (birthdayList).
Finally, good old bind(this) for the handlers that go off into event details and profile pages based on taps on the list.
Now we are into setup() where we make sure our super-visor gets setup too, and then tells the various lists and menus to go set themselves up too.
Command Menu
Now we setup the command menu for the events and birthday buttons at the bottom. Pretty standard:
setupCommandMenu: function() {
this.cmdMenuModel = {
visible: true,
items: [
{},
{
items: [
{ label: $L("Events"), command: "showEventsList" },
{ label: $L("Birthdays"), command: "showBirthdaysList" }
],
toggleCmd: "showEventsList"
},
{}
]
};
this.controller.setupWidget(Mojo.Menu.commandMenu, {}, this.cmdMenuModel);
},
We also need to handle the commands that come in (which we named showEventsList and showBirthdaysList):
handleCommand: function($super, event) {
$super(event);
if (event.type === Mojo.Event.command) {
switch (event.command) {
case "showEventsList":
this.showEventsList();
event.stopPropagation();
break;
case "showBirthdaysList":
this.showBirthdaysList();
event.stopPropagation();
break;
}
}
},
Notice the event.stopPropagation(). This stops the command propogation. If you ever see something like this in your logs, then remember to do this:
warning: command: Propagated up to AppAssistant, no command handler detected for showEventsList
Playing with the lists
We first setup the lists. Here is an example for birthdays:
setupBirthdaysList: function() {
this.birthdayListEl = this.controller.sceneElement.querySelector('#listBirthdays');
this.controller.setupWidget("listBirthdays", {
itemTemplate: "events-list/birthdays-item-template",
emptyTemplate: "events-list/birthdays-empty-template",
dividerTemplate: "events-list/birthdays-divider-template",
dividerFunction: this.eventsDividerFunction.bind(this),
hasNoWidgets: true
}, this.birthdayList);
this.eventManager.register(this.birthdayListEl, Mojo.Event.listTap, this.gotoProfileViewHandler);
},
The divider aims to be smart and show the birthdays starting from today and then wrapping forward (instead of starting from Jan 1st which would be the default):
eventsDividerFunction: function(item) {
var now = new Date();
var birthday = new Date(item.rawBirthdayDate);
if (birthday.getFullYear() == now.getFullYear()) { // this year differs from next year
return Mojo.Format.formatDate(birthday, $L("MMMM"));
} else {
return Mojo.Format.formatDate(birthday, $L("MMMM, yyyy"));
}
},
Now we are to a point where the commands can actually cause the lists to be shown. What we do here is manage the show/hide logic between the two lists. We also add in a bit of UX so if you tap on the event that you are already showing, it jumps to the top (a nice touch).
Also, here is where the rub comes. We saw weird behavior where the scroll state wasn’t reset as lists are shown and hidden. This means that if you scroll down in birthday and tap back to events, you may be seeing low down events (matching the scroll state of birthdays).
If you run into this, then you will want to do something like we do here:
- stop the state for each list via
this.controller.sceneScroller.mojo.getState() - when you show and hide, make sure to tell Mojo what you are doing via
this.controller.hideWidgetContainer(this.eventsListEl)
Here is the code that puts this all together for birthdays:
showBirthdaysList: function() {
// if you are already showing the birthdays list, go to the top of the list and exit
if (this.showing == "birthdays") {
this.controller.sceneScroller.mojo.revealTop();
this.birthdayListState = this.controller.sceneScroller.mojo.getState(); // should be 0, 0
return;
}
// you were showing events, so now turn on birthdays
this.showing = "birthdays";
this.eventsListState = this.controller.sceneScroller.mojo.getState(); // save off last state
this.controller.hideWidgetContainer(this.eventsListEl);
this.eventsListEl.hide();
this.birthdayListEl.show();
this.controller.showWidgetContainer(this.birthdayListEl);
// if there is some saved state, go ahead and set it
if (this.birthdayListState) {
this.controller.sceneScroller.mojo.setState(this.birthdayListState);
} else {
this.controller.sceneScroller.mojo.revealTop();
}
},
And there we have it.







