Helma Logo
main list history
previous version  overview  next version

Version 15 by Lehni on 08. May 2006, 22:51

Read *Helma 2 Templates - juerg* for a short overview of my efforts towards a more flexible templating engine in Helma (2).

The JavaScript Tempalte Engine is implemented in Helma 1.5, not Helma 2, but functions as a testcase for exploration of concepts for future releases. This doesn't stop me from using it in real websites as well, and so far it is working very well for me. It actually took away the urge to solve the problem.

The implementation is based on my comments in *06-12-05|http://helma.org/pipermail/helma-dev/2005-December/002461.html*. As far as I know, the implementation is fully backward compatible with Helma 1 skins and macros.


0.12  Fixed various bugs that were introduced in the 0.11 rewrite.
0.11  Replaced all the dirty hacks for keeping track of template line numbers
      and linking them to code line numbers by a clean implementation of the
      same functionality. The result is a faster and less resource hungry parser.
      Cleaned up the code, seperated tag parser form line parser.
0.10  More speed improvements, parsing is now around 6-7 times faster than in 0.8
0.9  Various speed improvements, leading to an overall decrease of parsing time
      by more than factor 3
0.8  Added support for HopObject collections in foreach
      Removed regexp filter for if() expressions that sometimes seemed to deadlock
      Fixed a bug with finding the right template in the inheritance chain if it
      was overriden by another one.
0.7  Fixed problems with error reports. Full stacktraces are now printed, and errors
      caused in macros called from the template are now properly detected and reported.
      Switched to using Context.evaluateString() instead of eval(), for improve of speed
      and reception of proper exceptions.
0.6  fixed a bug that caused macros in  objects other than 'this' and 'root' to fail.
0.5  first public release.

The file needs to be placed in the global protoype, it adds one function to the HopObject prototype:

<tt>HopObject.renderTemplate(name, param, out)</tt>

* name is the name of the skin, without the extension. "jstl" is used as extension for the template files, which stands for "JavaScript Template Language".
* param is the optional parameter, which can be accessed from the templates, just like in Helma skins.
* out is the writer object where the whole template is written to. if none is specified, renderTemplate renders into a StringBuffer and returns its value as a string.

====Overview of Statements

* <tt>&lt;% foreach (object in list) %&gt; ... &lt;% end %&gt;</tt>: loops over the list, on purpose not named "for (object in list)", because object won't be the index but the object itself
* <tt>&lt;% if () %&gt;, &lt;% elseif () %&gt;, &lt;% else %&gt;, &lt;% end %&gt;</tt>
* <tt>&lt;% set variable = otherVariable [ + some arithmetic ] %&gt;,</tt> e.g. <tt>&lt;% set count = this.nodes.count() %&gt;</tt>
* <tt>&lt;% macro foo="bar" array=[1,2,3] hash={a:1, b: 2, c:3} %&gt;</tt>: calls the macro, both global macros and object based macros are supported (e.g. this.macro, root.macro, etc.). prefix, suffix, default are supported. It is supposed to be fully backward compatible with Helma 1 skins.
* <tt>&lt;% macro &gt;nested content&lt;/%&gt;</tt>: passes the nested content to the macro, as a second parameter.
* <tt>&lt;% param.value %&gt;</tt>: prints out the value, response, request, sesion, param are suppored, as links to res.data, req.data, session.data, param. This is here for backward compatibility. The prefered way of printing data is:
* <tt>&lt;%= param.object.href() %&gt;</tt>: prints out the value or the result of a function call. response, request, session do NOT link res.data, req.data, session.data.


Conditional statements, expressions:

  <% if (session.user != null) %>
    Hello <%= session.user.name %>
  <% else %>
    <a href="/login">Login</a>
  <% end %>


  <% foreach (topic in param.topics) %>
    <tr class="topic">
      <td><%= topic.renderLink() %></td>
      <td><%= topic.creator.renderLink() %></td>
      <% set count = topic.comments.count() - 1 %>
      <% if (count == 1) %>
        1 Post
      <% elseif (count > 1) %>
        <%= count %> Posts
      <% else %>
      <% end %>
      <td nowrap><%= topic.createDate.format("dd.MM.yy - HH:mm") %></td>
  <% end %>