[ Home | About | Syntax | Examples | Unit Tests | License ]
The DUEL grammar is an intentionally small, familiar syntax. Each term is intended to be intuitive to remember and is short without abbreviations. DUEL views are defined as HTML/CSS/JavaScript with a small set of special markup tags to control flow and JavaScript code blocks to bind the view template to model data.
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.
DUEL code blocks contain 100% pure JavaScript, so there isn't a new language to learn. Since DUEL uses a familiar syntax for code blocks, syntax highlighting in editors is widely supported out of the box. There are three types of code blocks available:
<% /*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.
The ambient JavaScript data values made available inside code blocks are:
data
: contains the model data being bound to the view template
index
: used within all loops to indicate the item index of the current data
being bound
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 data
being bound
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 index
and count
values will be updated with the current item index and total item count, respectively.
If the each
attribute does not contain an Array
expression, the loop will treat it as if it were the first item in an Array
.
<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 index
and 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 index
and 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 <hr>
and <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="…">
.
<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 key
, index
and count
attributes may be passed through if the view is being called within a loop (e.g. item index
of count
items).
The 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 <view>
and <part>
. Since it is always a string,
<part name="foo"></part>
is effectively equivalent to:
<part name="<%= "foo" %>"></part>