part 5
Often, you’ll be using one of FOAM’s generic controllers, which can handle
navigation for you. In this tutorial, we’ll do navigation by hand to demonstrate
more advanced templates, the View lifecycle, and other concepts.
We’ll begin by expanding the ControllerView to make the decision of whether to
show us a single phone’s page or the list. Expand it to look like this:
CLASS({
name: 'ControllerView',
extendsModel: 'DetailView',
requires: [
'PhoneCitationView',
'PhoneDetailView'
],
templates: [
function toHTML() {/*
<% if ( window.location.hash ) {
var view = PhoneDetailView.create({ model: Phone });
this.addChild(view);
this.data.dao.find(window.location.hash.substring(1), {
put: function(phone) {
view.data = phone;
}
});
return view.toHTML();
} else { %>
Search: $$search
Sort by: $$order
<p>$$filteredDAO{className: 'phones', tagName: 'ul'}</p>
<% } %>
*/}
],
methods: {
init: function() {
this.SUPER();
window.addEventListener('hashchange', function() {
document.body.innerHTML = this.toHTML();
this.initHTML();
}.bind(this));
}
}
});Note that our original ControllerView template is now the else branch, ie.
what will be shown when window.location.hash is empty.
- We’re navigating by setting
window.location.hashto theidof the phone we want to see. - We can put real Javascript control structures inside
<% %>tags, and indeed theifbranch is written entirely in Javascript. - In that first branch, we create a
PhoneDetailView, which we’ll define shortly. We tell it whatmodelit should be the view for, but not what object is currently being viewed. - All views have
addChild, which adds another view to a list of child views. The defaultinitHTML(see below) will callinitHTML()on all the child views recursively. - We look up the phone in the master
dao, not the filtered one.find(id, sink)will locate a single item by its ID.- The
idof the phone we want to find is the hash, with the leading#chopped off. - We provide a custom
sink, which is just a Javascript object with aputfunction.putwill be called, if defined, when a DAO finds an object matching a query. - When the phone is found, the DAO will call our sink’s
putfunction with thePhoneobject. Ourputwill setview.datato the phone, and thePhoneDetailViewwill automatically update itself to display this new data.
- The
- We also add an
initmethod. This is similar to a constructor and is called duringControllerView.create(). It is very important to callthis.SUPER()first thing in any custominitwe write, since the parent classes do several important things ininit. - Our
initadds a listener to thehashchangeevent, which will re-render the template into the page. DOM templates have a two-step lifecycle:toHTML()is the main template function. It returns a string which gets added to the DOM.initHTML()needs to run after everything is added to the DOM. It hooks up any listeners, callsinitHTMLrecursively on children, and so on. You generally won’t need to write custominitHTMLmethods.
Now we need to define PhoneDetailView. As we did before, let’s simply define
it as an empty subclass of DetailView:
CLASS({
name: 'PhoneDetailView',
extendsModel: 'foam.ui.DetailView'
});That will be enough to let us reload the page and see it working, though it will be ugly as before.
Try it! Click on a phone in the catalog, and you will switch to its detail view. Use your browser’s Back button, and you’re back at the catalog.
Part 6 shows external templates and
explores DetailViews further.