Syntax

DUEL Syntax

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.

Code Blocks

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:

Statement Blocks

<% /*statements*/ %>

When code within a statement block is executed, if any value is returned it will be emitted as part of the output.

Expression Blocks

<%= /*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*/); %>

HTML Expression Block

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 Values

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

Command Markup

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 declaration

Syntax:
<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).

Example:
<view name="example.Foo">

<p>Hello world.</p>

Looping and iteration

Array iteration

Syntax:
<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.

Example:
<ul><for each="data.items">
	<li>Item <%= data %> is <%= index %> of <%= count %>.</li>
</for></ul>

Property iteration

Syntax:
<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.

Example:
<ul><for in="data.foo">
	<li>Property named <%= key %> has the value <%= data %> and is <%= index %> of <%= count %>.</li>
</for></ul>

Counting iteration

Syntax:
<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.

Example:
<ul><for count="4" data="data.foo">
	<li>The same <%= data %> for <%= index %> of <%= count %> items.</li>
</for></ul>

Conditional logic

Conditional blocks

Syntax:
<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="…">.

Example:
<if test="data === 0">
	<p>Zero</p>
<else if="data === 1">
	<p>One</p>
<else>
	<p>Many</p>
</if>

Single element conditional

Syntax:
<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.

Example:
<div if="data.items.length === 0">
	<p>Sorry, no results were found.</p>
</div>

Embed other views

Syntax:
<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.

Example:
<call view="example.Bar" data="data.details" />

Partial views

Syntax:
<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.

Example:
<call view="example.Bar" data="data.details">
<part name="header"><h2><%= data.title %></h2></part>
</call>

Command Arguments

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>

Updated

Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.