Skip to content

Templates

Kevin Greer edited this page Oct 16, 2024 · 1 revision

FOAM Templates

Introduction

Perhaps no other area of software development has been as immune to positive advancement as templating systems. Most templating systems have not advanced beyond 60's-era macro processing systems; favouring 'partials' instead of procedures or methods as a method of reuse, maps of key-value pairs instead of sturctures or objects, and cryptic single-character control characters like #, /, !, &, %, $, etc., instead of modern ifs and for loops. Modern methods for structuring code for reusability and maintainability, such as encapsulation, polymorphism, inheritance, and composition, are ususally completely absent.

FOAM Templates bring templating into the modern object-oriented era. This helps to make them easier to use and allows for the creation of maintainable and reusable templates. Modern software-engineering best practices, such as: structured-programming, code-reuse, encapsulation, inheritance, polymorphism, composition, decoration, etc. are all available with FOAM Templates. As a result, FOAM Templates are extensible, high-performance, memory-efficient, reusable, maintainable, and offer improved developer productivity.

##Template Syntax

FOAM's template system uses the standard Java Server Pages (JSP) template format, with the exception that code segments are expressed in Javascript instead of Java. Additionally, the {{ and {{{ syntax from the Mustache template system is also supported.

  • <% code %>: code inserted into template, but nothing implicitly output.

  • <!-- comment -->: XML-style comment, not included in output.

  • <%= comma-separated-values %>: all values are appended to template output, without HTML escaping.

  • <%# dynamic value %>: the value is output and then dynamically updated if it changes:

      item<%# this.value.value.activeCount == 1 ? '' : 's' %>
    

    will add or remove the 's' when activeCount changes.

  • %%<value>: short for <%= this.<value> %>, where <value> can be either a property or a method call

  • $$<propertyName>: short for <%= this.createTemplateView('propertyName').toHTML() %>, used to embed sub-views. For DetailViews, it will look for a property in the view's data object, and if it doesn't exist, then it will look for the property in the DetailView itself.

  • $$<actionName> embeds an ActionButton for the named action. For DetailViews, it will look for an action in the view's data object, and if it doesn't exist, then it will look for the action in the DetailView itself.

  • {{comma-separated-values}}: same as <%= but one character shorter, uses handlebars syntax, output is escaped (unlike <%)

  • {{{comma-separated-values}}}: same as <%= but one character shorter, uses handlebars syntax, output is not escaped (like <%)

  • <new-line>: ignored

The standard JSP syntax of <%! %> for declarations is not used because Javascript does not have different modes for declaring variables and code. Just use the <% %> tag for declaring local variables. (Declare class variables and methods in your model, just as you normally would.)

<%= value1, value2, ... valueN %> is the same as: <% out(value1, value2, ..., valueN); %>

Configuring $$ Views and Actions

Sub-views or Actions defined with the $$ syntax can be configured by providing an optional parameter map.

Ex.

$$filteredDAO{tagName: 'ul', elementId: 'todo-list'}

Defined in Models

Templates can be defined in-line in Models. Three separate forms are allowed:

   templates:[
     {
        model_: 'Template',

        name: 'toHTML',
        description: 'TileView',
        template: '<div class="gridtile"><table cellspacing="0" cellpadding="0"><tbody><tr><td class="id"><img src="https://ssl.gstatic.com/codesite/ph/images/star_off.gif"><a href="https://code.google.com/p/chromium/issues/detail?id=<%= this.issue.id %>"><%= this.issue.id %></a></td><td class="status"><%= this.issue.status %></td></tr><tr><td colspan="2"><div><a href="https://code.google.com/p/chromium/issues/detail?id=<%= this.issue.id %>"><%= this.issue.summary %></a></div></td></tr></tbody></table></div>'
     },
   ]

   templates:[
     {
        model_: 'Template',

        name: 'toHTML',
        description: 'TileView',
        template: function() {/*
           <div class="gridtile">
             <table cellspacing="0" cellpadding="0">
             ...
             </table>
           </div>
        */}
     },
   ]

   templates:[
     function toHTML() {/*
       <div class="gridtile">
         <table cellspacing="0" cellpadding="0">
           ...
         </table>
      </div>
     */}
   ]

The second two formats allow you to more easily embed multi-line templates.

External Templates

You may prefer to store larger templates in an external file. To do this, just leave out the 'template:' portion of the template JSON and instead put the template in an external file named: <Model Name>_<Template Name>.ft. For example, the above template, taken from the CIssueTileView model, would appear in a file named CIssueTileView_toHTML.ft.

Ex. The following template adds the toHTML method to the CIssueTileView model, so appears in a file called CIssueTileView_toHTML.ft

from: https://code.google.com/p/foam-framework/source/browse/apps/crbug/CIssueTileView_toHTML.ft

<%
  var starView = CIssue.STARRED.view.create();
  starView.value = this.issue.propertyValue('starred');
  this.addChild(starView);
%>
<div class="gridtile">
  <table cellspacing="0" cellpadding="0"><tbody>
    <tr>
      <td class="id">
        {{{ starView.toHTML() }}}
        <a href="https://code.google.com/p/chromium/issues/detail?id={{this.issue.id}}">{{this.issue.id}}</a>
      </td>
      <td class="status">{{this.issue.status}}</td>
    </tr>
    <tr>
      <td colspan="2">
        <div><a href="https://code.google.com/p/chromium/issues/detail?id={{this.issue.id}}">{{this.issue.summary}}</a></div>
      </td>
    </tr>
  </tbody></table>
</div>

Limitations of External Templates

Attempting to use external templates when loading files using the file: protocol, will not work. To work around this problem, either serve your files from a local HTTP server (Ex. python -m SimpleHTTPServer), or else run Chrome with the --allow-file-access-from-files option.

Control Structures

JSPs do not try to invent their own limited and proprietary control structures, but instead reuse the more powerful built-in controls structures of the host language (in our case: Javascript). This saves the developer from having to learn and remember new incompatible and unusually more limited syntax.

Conditionals

<% if ( this.enabled ) { %>Enabled<% } %>

or

<% if ( this.enabled ) { %>
  Enabled
<% } %>

or

<% if ( this.enabled ) { %>
  Enabled
<% } else { %>
  Disabled
<% } %>

or

<%= this.enabled ? 'Enabled' : 'Disabled' %>

Loops

The following two examples output the numbers from one to ten.

<% var i = 0; while ( i++ < 10 ) { %>
  <%= i %>
<% } %>

or

<% for ( var i = 0 ; i < 10 ; i++ ) { %>
  <%= i %>
<% } %>

Recursion is also supported.

Nesting Templates

Templates are compiled to regular methods, so hacks like includes, partial-templates, etc. are not required. To call a second template from the first, it's just a simple method call.

Ex.

template1
blah blah blah
<% this.template2(out) %>
blah blah blah

Note that the following would also work:

<%= this.template2() %>

But this has the disadvantage, common to many templating systems, that it would result in re-copying generated output once for each level of nesting. This would mean that your template would perform and generate garbage in O(size*depth). FOAM Templates' approach of passing the output stream down instead, means that it is only O(size).

A short form for for the above would be just:

%%template2()

Sub-Views

When calling a sub-view, if this extends View, then the sub-view will automatically be added as a child so that its initHTML() method will be called when this.initHTML() is called.

Ex.

<div class="searchBar">
  Search %%searchChoice for %%searchField %%linkButton %%countField
</div>

Escaping HTML

The simplest way to escape your output as HTML is the use the {{ }} tag.

Ex.

{{"<b>bold</b>"}}

Will generate:

&lt;b&gt;bold&lt;/b&gt;

Another method is to do:

<%= escapeHTML("<b>bold</b>") %>

Debugging

To set a breakpoint in a template, add the following: <% debugger; %>

Editor Support

FOAM templates use the standard JSP syntax, so syntax highlighting is available for many common editors.

For Emacs, see http://www.emacswiki.org/emacs/JspMode.

Also, you should generally have very little code in your templates, so HTML highlighting should normally be sufficient.

Advantages

Small Size

The implementation of FOAM templates is only 104 lines of code, making it much smaller than other templating systems. This decreases download sizes and memory usage.

Low Download Size

FOAM Templates are compiled on the client-side, which means that the smaller template source, rather than the larger compiled template is transferred. This saves bandwidth which improves application load and start times.

Extensible

FOAM Templates are built using FOAM's modular parsing framework. This means that the grammar is open to extension without requiring modification to the default grammar. This allows the templating system to be extended at the library level. New tags can be easily added as needed by libraries or applications. As an example, it only required three extra lines of code to add the Handlebars-like {{ }} and {{{ }}} tags.

Object-Oriented

Most templating systems are not object-oriented, and as a result, are not as easy to use as they could be, and incur an impedance mismatch when working with objects. It is common for templating systems to be passed in a map/hash/scope/context of data to be used to populate the data. FOAM Templates on the other hand, just use this. This avoids extra work on the part of both the developers writing the code, and the computers executing it. Furthermore, it allows easy access to other methods of the object, without the need for the methods to be bound and copied one-by-one into a map.

O(size) Nesting

As mentioned above under "Template Nesting", FOAM allows you to nest templates without generating extra garbage or incurring extra copying. This is important, not only for performance reasons, but also for maintainability reasons. You can properly factor you templates without having to worry about performance degredation.

Garbage-Free

FOAM Templates OO design eliminates the needless generation of scope objects, which reduces garbage generation. FOAM's ability to nest templates without incurring extra coping also eliminates the generation of extra garbage at each level. Garbage elimination is important because garbage takes CPU resources to both generate and then subsequently correct, slowing down your program.

High-Performance

FOAM, in general, is a very high-performance framework, and as a result, FOAM Templates need to be equally fast in order to not become the bottleneck.

FOAM Templates achieve their high-level of performance from a number of design decisions:

  1. FOAM Templates are compiled, not interpreted.
  2. FOAM's O(size) template nesting.
  3. Minimizing garbage-generation.
  4. JIT-able. Because FOAM templates are compiled directly to methods, and calls to other templates are also methods, the Javascript JIT can in-line the code and eliminate dynamic dispatch in most cases, which of course, greatly improves performance. Templating systems which rely on non-Object scopes for supplying data, can not be JITed and can suffer dramatically worse performance as a result.

Inheritance Friendly

If a FOAM Template nests calls to other templates, those sub-templates can be overridden in sub-classes without requiring the top-level template to be changed.

Polymorphism

If a FOAM Template nests calls to templates belonging to other objects, then the template selection will be polymorphic with respect to that other object's class. Just like with "inheritance", this approach contributes to template reuse and maintainability.

Lazy Compilation

For hosted apps, FOAM can defer template compilation until they are first used, which saves memory.

Packaged Application Compatible

FOAM Templates are the only dynamic templating system to work with Chrome packaged-apps. Given that Chrome apps are an important use-case for FOAM, this is of critical importance.

Simplified Build Process

Since templates are dynamically compiled at run-time, no build step is required, which shortens the development cycle and increases developer productivity.

Simple

FOAM Templates are much simpler to use than other templating systems. The control-structures are the standard Javascript structures that developers already know, and templates are called as just regular methods. No knowledge of compiling or binding to templates is needed.

Standard

FOAM Templates are based on the extremely popular JSP standard, which means that the syntax is already familiar to many developers, and most programming editors will have a mode for editing and providing syntax-colouring. There are literally dozens of books available on JSPs.

References: