[ Home | About | Syntax | Examples | Unit Tests | License ]
The goal is to deliberately keep the syntax minimal. It should be easy to remember and nothing should need to be looked up during usage.
<% /*statements*/ %>
When code within a statement block is executed, if any value is returned it will be emitted as part of the output.
<%= /*expression*/ %>
When code within an expression block is executed, the result of will be emitted as part of the output, interpretted as plain text. Expression blocks are effectively equivalent to:
<% return (/*expression*/); %>
Note: these blocks are rarely needed; only when data itself contains markup. Care must be taken to avoid script injection with markup blocks.
<%# /*expression*/ %>
When code within a markup expression block is executed, the result will be emitted as part of the output, interpreted as HTML.
data: contains the model data being bound to the view template
index: used within all loops to indicate the item index of the current
count: used within all loops to indicate total number of items being bound
key: used within property loops to indicate the property name of the current
DUEL reduces potential for "tag soup" by controlling loops and conditionals declaratively through a small set of memorable markup commands. The complete set of DUEL command markup is:
<view name="…"> … </view>
Sits at the top of a view to define its metadata. The
name attribute contains a string literal which defines the name of view type. This optionally may have a close tag at the end of the view.; open views will be auto-closed by the compiler.
A rarely used feature of this tag is for defining multiple views in the same file. By closing the
</view> tag and opening another, a second named view may be defined.
This tag is the only child of the file, and may only sit at the root. Views may not be nested, although they may call into one another (See the
<call /> command).
<view name="example.Foo"> <p>Hello world.</p>
<for each="…"> … </for>
Wrapped around content to be repeated once per item. The
each attribute contains an
Array expression defining the list of items to iterate over. The
data value will contain the current item, and the
count values will be updated with the current item index and total item count, respectively.
each attribute does not contain an
Array expression, the loop will treat it as if it were the first item in an
<ul><for each="data.items"> <li>Item <%= data %> is <%= index %> of <%= count %>.</li> </for></ul>
<for in="…"> … </for>
Wrapped around content to be repeated once per property. The
in attribute contains an
Object expression, and the loop will iterate over the properties of that object. In this case,
data will contain the property value and an additional
key value will contain the (
String) property name. The
count values will be updated with the current property index and total count of properties, respectively.
<ul><for in="data.foo"> <li>Property named <%= key %> has the value <%= data %> and is <%= index %> of <%= count %>.</li> </for></ul>
<for count="…" data="…"> … </for>
Wrapped around content to be repeated
count number of times. The
count attribute contains a
Number expression, and the loop will iterate that number of times. If the
data attribute is not specified,
data will contain the outer scope's
data value otherwise it will contain the result of the
data attribute expression. The
count values will contain the current index and the result of the
count attribute expression, respectively.
<ul><for count="4" data="data.foo"> <li>The same <%= data %> for <%= index %> of <%= count %> items.</li> </for></ul>
<if test="…"> … <else if="…"> … <else> … </if>
Wrapped around conditional content. The
test attribute contains a
boolean expression indicating if contents should be included in result. Any truthy value will cause the contents of that section to be emitted.
<else> tags sit inside an
<if></if> block as dividers without closing tags (similarly to
<br> in HTML). The
if attribute contains a
boolean expression indicating if contents should be included in result. Alternatively,
<else test="…"> may be used for symmetry with
<if test="data === 0"> <p>Zero</p> <else if="data === 1"> <p>One</p> <else> <p>Many</p> </if>
<div if="…"> … </div>
May be applied to any HTML tag to make it conditionally render. The
if attribute contains a
boolean expression indicating if contents should be included in result.
<div if="data.items.length === 0"> <p>Sorry, no results were found.</p> </div>
<call view="…" data="…" key="…" index="…" count="…" defer />
Calls another template specifying the data to bind. The
view attribute is the name of the view to bind, the
data attribute defines the data to bind. Optionally
count attributes may be passed through if the view is being called within a loop (e.g. item
defer attribute is an optional mechanism to express that the call should be deferred and executed client-side.
<call view="example.Bar" data="data.details" />
<part name="…"> … </part>
Sits inside a view as a placeholder for replacement content, or within a
<call></call> block to define the replacement content. The
name attribute is a
string expression specifying the name of the part to replace.
<call view="example.Bar" data="data.details"> <part name="header"><h2><%= data.title %></h2></part> </call>
A shorthand syntax is available for the attributes of markup commands. The attributes in each of the markup elements are implicitly code blocks. This means you can add or leave off the code block syntax based on preference:
<if test="index % 2 === 0"> <%= index %> is even. <else> <%= index %> is odd. </if>
is equivalent to:
<if test="<%= index % 2 === 0 %>"> <%= index %> is even. <else> <%= index %> is odd. </if>
The exception to this shorthand is the
name attribute of
<part>. Since it is always a string,
is effectively equivalent to:
<part name="<%= "foo" %>"></part>