part 4
A view is responsible for presenting some data to the user. This might be a single object, or a collection.
In FOAM, a View needs to specify some HTML, by returning a Javascript string
from a method called toHTML().
Rather than writing a Javascript function that constructs and returns such a
string, FOAM supports a template syntax. A class can have a templates section,
and at load time your templates are compiled into Javascript functions that
return strings.
Templates can also live in external files, which are introduced in part 6.
Template Syntax
FOAM’s template syntax is a superset of JSP syntax. This means templates are HTML overall, with the following extras:
<% %>encloses Javascript code.- Real Javascript, not a restricted subset.
<%= %>encloses a comma-separated list of Javascript expressions whose values are written to the DOM.%%fooinserts the simple value ofthis.foointo the page.$$fooinserts theviewforthis.fooat this location. That is, whatever thefooproperty’sviewis set to, one will be created and inserted here.$$foo{ x: 'abc', y: 'def' }will pass along those values to.createwhen it builds the view forfoo.
Control Structures
Since <% %> allows embedding arbitrary, real Javascript code, you can create
control structures like so:
<ul>
<% for (var i = 0 ; i < list.length; i++ ) { %>
<li><%= list[i] %></li>
<% } %>
</ul>However, code like this is rarely necessary, since FOAM contains many views that
handle creating rows from a collection of data. DAOListView from
part 3 is one example; TableView and
GridView are two more.
Inline Templates
Let’s define the template for each phone in the catalog. Expand
PhoneCitationView so it looks like this:
CLASS({
name: 'PhoneCitationView',
extendsModel: 'DetailView',
templates: [
function toHTML() {/*
<li class="thumbnail">
<a href="#{{this.data.id}}" class="thumb">$$imageUrl</a>
<a href="#{{this.data.id}}">$$name{mode: 'read-only'}</a>
<p>$$snippet{mode: 'read-only'}</p>
</li>
*/}
]
});- FOAM uses multi-line comments in templates as a hack for multi-line strings in Javascript.
- Inside a template,
thisis bound to the view itself, not thePhoneobject. - The
Phoneobject isthis.datainstead. - The
$$foosyntax puts a child view at this location.imageUrlhas itsviewset toImageView, so$$imageUrlwill render anImageViewinside the first link tag. $$name{mode: 'read-only'}will put the child view for thenameproperty (defaults toTextFieldView) in the second link tag, creating the view with itsmodeproperty set to'read-only'.mode: 'read-only'on aTextFieldViewis simply text in a<span>.- Similarly for
$$snippet{mode: 'read-only'}.
Now reload your app and see that… it’s a complete mess. That’s because PhoneCitationView is putting in <li> tags but they’re not in a <ul>, and the custom CSS for the app is not being loaded.
We’ll get back to the CSS shortly. First, let’s add a second template, for the top-level ControllerView. Add this code to Controller.js, expanding our ControllerView:
CLASS({
name: 'ControllerView',
extendsModel: 'DetailView',
requires: [
'PhoneCitationView',
'foam.ui.TextFieldView',
'foam.ui.ChoiceView',
'foam.ui.DAOListView',
'foam.ui.ImageView'
],
templates: [
function toHTML() {/*
Search: $$search
Sort by: $$order
<p>$$filteredDAO{ className: 'phones', tagName: 'ul' }</p>
*/}
]
});- Most FOAM views support
classNameandtagName. The defaulttagNamefor aDAOListViewis<div>, but we want to use<ul>here. searchhasviewset toTextFieldView, so it will render as a text box.order’sviewisChoiceView, which renders a drop-down list.filteredDAOis theDAOListView, which renders the list of entries.
The custom CSS still isn’t loaded, so add the following to index.html’s
<head> tag:
<link rel="stylesheet" href="css/app.css" />
<link rel="stylesheet" href="css/bootstrap.css" />and reload your app. Now it should look much better, and the search and sort functions work!
Part 5 will add navigation to our app.