Marko

Core tags and attributes

Marko provides a number of tags

Control flow

<if>, <else-if>, <else>

The <if>, <else-if>, and <else> tags provide conditional control-flow for templates.

<if(arriving)>
    <div>Hey there</div>
</if>
<else-if(leaving)>
    <div>Bye now</div>
</else-if>
<else>
    <div>What's up?</div>
</else>
if(arriving)
  div -- Hey there
else-if(leaving)
  div -- Bye now
else
  div -- What's up?

Conditionals may also be applied as attributes:

<div if(arriving)>Hey there</div>
<div else-if(leaving)>Bye now</div>
<div else>What's up?</div>
div if(arriving) -- Hey there
div else-if(leaving) -- Bye now
div else -- What's up?

And support complex expressions:

<if(Math.random() > 0.5)>
    <div>50-50 chance of seeing this</div>
</if>
if(Math.random() > 0.5)
  div -- 50-50 chance of seeing this

<for>

The <for> tag allows iterating over an array of items:

<ul>
    <for(color in colors)>
        <li>${color}</li>
    </for>
</ul>
ul
  for(color in colors)
    li -- ${color}

It may also be applied as an attribute:

<ul>
    <li for(color in colors)>${color}</li>
</ul>
ul
  li for(color in colors) -- ${color}

With either of the above templates, and the following value for colors:

var colors = ['red', 'green', 'blue'];

The output HTML would be the following:

<ul>
    <li>red</li>
    <li>green</li>
    <li>blue</li>
</ul>

Loop Status Variable

The for directive also supports a loop status variable in case you need to know the current loop index. For example:

<ul>
    <li for(color in colors | status-var=loop)>
        ${color}
        ${loop.getIndex()+1}) of ${loop.getLength()}
        <if(loop.isFirst())> - FIRST</if>
        <if(loop.isLast())> - LAST</if>
    </li>
</ul>
ul
  li for(color in colors | status-var=loop)
    --- 
    ${color}
    ${loop.getIndex()+1}) of ${loop.getLength()}
    ---
    if(loop.isFirst()) -- - FIRST
    if(loop.isLast()) -- - LAST
Loop Status Methods
getLength()

Returns the length of the array

getIndex()

Returns the current loop index

isFirst()

Returns true if the current index is the first index, otherwise false

isLast()

Returns true if the current index is the last index, otherwise false

Loop Separator

Used for separating values in a loop by characters. The first element will not be prefixed and the last element will not be suffixed with the separator:

<div>
    <!-- Output: red, green, blue -->
    <span for(color in colors | separator="") style="color: ${color}">
        ${color}
    </span>
</div>
div
  // Output: red, green, blue
  span for(color in colors | separator="") style="color: ${color}" -- ${color}

Range Looping

A range can be provided in the following format; <var-name> from <from> to <to>[ step <step>].

The from, to and step values must be numerical expressions. If not specified, step defaults to 1.

<ul>
    <li for(from 0 to 10)>
        ${i}
    </li>
</ul>
<ul>
    <li for(from 0 to 10 step 2)>
        &#36;{i}
    </li>
</ul>
<ul>
    <li for(from 0 to myArray.length-1)>
        ${myArray[i]}
    </li>
</ul>
ul
  li for(from 0 to myArray.length-1) -- ${myArray[i]}

Property Looping

<ul>
    <li for(name, value in settings)>
        <b>${name}</b>${value}
    </li>
</ul>
ul
  li for(name, value in settings)
    -- <b>${name}</b>${value}

Native JavaScript for-loop

<for(var i=1; i<=3; i++)>
    ${i}
</for>
for(var i=1; i<=3; i++) -- ${i}

Custom Iterator

static function reverseIterator(arrayList, callback) {
    for(var i=arrayList.length-1; i>=0; i--){
        callback(arrayList[i]);
    }
}
 
<div for(item in ['a''b''c'] | iterator=reverseIterator)>
    ${item}
</div>
static function reverseIterator(arrayList, callback) {
    for(var i=arrayList.length-1; i>=0; i--){
        callback(arrayList[i]);
    }
}
div for(item in ['a''b''c'] | iterator=reverseIterator) -- ${item}
Output:
<div>c</div>
<div>b</div>
<div>a</div>

<while>

Any element can be repeated until a condition is met by using the while directive. The directive can be applied as an element or as an attribute.

Applied as an attribute:

$ var n = 0;
 
<ul>
    <li while(n < 4)>
        ${n++}
    </li>
</ul>
$ var n = 0;
ul
  li while(n < 4) -- ${n++}

Applied as an element:

$ var n = 0;
 
<ul>
    <while(n < 4)>
        <li>${n++}</li>
    </while>
</ul>
$ var n = 0;
ul
  while(n < 4)
    li -- ${n++}

body-only-if

If you find that you have a wrapper element that is conditional, but whose body should always be rendered then you can use the body-only-if attribute to handle this use case. For example, to only render a wrapping <a> tag if there is a valid URL then you could do the following:

<a href=input.linkUrl body-only-if(!input.linkUrl)>
    Some body content
</a>
a href=input.linkUrl body-only-if(!input.linkUrl) -- Some body content

Given a value of "http://localhost/" for the input.linkUrl variable: , the output would be the following:

<a href="http://localhost/">
    Some body content
</a>
a href="http://localhost/" -- Some body content

Given a value of undefined for the input.linkUrl variable: , the output would be the following:

Some body content
Some body content

JavaScript

The following tags are always written using the concise syntax, even when using HTML syntax for tags that generate HTML output.

import

Static: The code generated by import will run once when the template is loaded and be shared by all calls to render. It must be declared as a top level tag and does not have access to data, state, or other values passed in at render.

The import tag is used to access data and functions from external files. It follows the same syntax as the JavaScript import statement.

import sum from './utils/sum';
<div>The sum of 2 + 3 is ${sum(23)}</div>
import sum from './utils/sum'
div -- The sum of 2 + 3 is ${sum(23)}

Reusable content

<include>

The include tag is used to embed another template (or part of another template) in the current template.

The include tag can take a path to a template as the first argument:

<include('./path/to/template.marko')/>
include('./path/to/template.marko')

The second argument may be used to pass input to the included template:

<include('./path/to/template.marko'{ name:'Frank' })/>
include('./path/to/template.marko'{ name:'Frank' })

Attributes may also be used to pass data to the included template and can be used in combination with the input parameter:

<include('./path/to/template.marko', data) name="Frank"/>
include('./path/to/template.marko', data) name="Frank"

You can also include imported UI compoments (or a reference to a load UI component):

import Hello from './components/hello/index.marko';
 
<include(Hello) name='Frank' />
 
// Include a reference to a UI component:
<include(input.myDynamicComponent) name='Frank' />
import Hello from './components/hello/index.marko'
include(Hello) name="Frank"
// Include a reference to a UI component:
include(input.myDynamicComponent) name="Frank"

Layouts with nested attributes

In addition to including external content, you can inject additional content chunks into the external content. This is accomplished by using nested attribute tags which are denoted by the @ symbol:

page.marko
<include('./layout.marko')>
    <@body>
        <h1>Hello Marko</h1>
    </@body>
</include>
include('./layout.marko')
  @body
    h1 -- Hello Marko

Then in your layout template you can include the injected content:

layout.marko
<!doctype html>
<html>
    <body>
        <!-- this comes from <@body> -->
        <include(input.body)/>
    </body>
</html>
<!doctype html>
html
  body
    // this comes from <@body>
    include(input.body)

<include-text>

<include-text('./foo.txt')/>
include-text('./foo.txt')

Special HTML characters will be escaped. If you do not want escaping then use the <include-html> tag (see below).

<include-html>

<include-html('./foo.html')/>
include-html('./foo.html')

Special HTML characters will not be escaped since the file is expected to be an HTML file.

<macro>

Parameterized macros allow for reusable fragments within an HTML template. A macro can be defined using the <macro> directive.

<macro greeting(name, count)>
    Hello ${name}! You have ${count} new messages.
</macro>
macro greeting(name, count) -- Hello ${name}! You have ${count} new messages.

The above macro can then be invoked as part of any expression. The following sample template shows how to use macro functions inside expressions:

<macro greeting(name, count)>
    Hello ${name}! You have ${count} new messages.
</macro>
<p>
    <greeting("John"10)/>
</p>
<p>
    <!-- Or, using named attributes: -->
    <greeting name="Frank" count=20/>
</p>
macro greeting(name, count) -- Hello ${name}! You have ${count} new messages.
p
  greeting("John"10)
p
  // Or, using named attributes:
  greeting name="Frank" count=20

Async content

<await>

The <await> tag is used to dynamically load in content from a data provider. The data provider can be a Promise or a callback. Once the provider returns it's results the children are rendered.

await-example.marko
$ var personPromise = new Promise((resolve, reject) => {
    setTimeout(function() {
        resolve({
            name: 'Frank'
        });
    }1000);
});
 
<await(person from personPromise)>
    <div>Hello ${person.name}!</div>
</await>
$ var personPromise = new Promise((resolve, reject) => {
    setTimeout(function() {
        resolve({
            name: 'Frank'
        });
    }1000);
});
await(person from personPromise)
  div -- Hello ${person.name}!

Advanced implementation:

  • <await> tag signature
    • Basic usage: <await(results from dataProvider)>...</await>
    • Optional attributes
      • client-reorder boolean
      • arg expression
      • arg-* string
      • method string
      • timeout integer
      • timeout-message string
      • error-message string
      • placeholder string
      • renderTimeout function
      • renderError function
      • renderPlaceholder function
      • name string
      • scope expression
      • show-after string
    • Optional child tags
      • <await-placeholder>Loading...</await-placeholder>
      • <await-timeout>Request timed out</await-timeout>
      • <await-error>Request errored</await-error>

Comments

Standard HTML comments can be used to add comments to your template. The HTML comments will not show up in the rendered HTML.

Example comments:

<!-- This is a comment that will not be rendered -->
<h1>Hello</h1>
// This is a comment that will not be rendered
h1 -- Hello
// You can also use standard JavaScript-style comments
/*
 Block comments are also supported
 */
-- Hello

If you would like for your HTML comment to show up in the final output then you can use the custom html-comment tag.

<html-comment>

input.marko
<html-comment>This is a comment that *will* be rendered</html-comment>
<h1>Hello</h1>
html-comment -- This is a comment that *will* be rendered
h1 -- Hello
output.html
<!--This is a comment that *will* be rendered-->
<h1>Hello</h1>

Alternatively, the <marko-compiler-options> tag may be used to configure comments for the entire template:

<marko-compiler-options preserve-comments/>
marko-compiler-options preserve-comments

Compiler options

marko-preserve-whitespace

Whitespace can be preserved using the preserve-whitespace attribute:

<div marko-preserve-whitespace>
    All of this
    whitespace   will
    be preserved.
</div>
<div marko-preserve-whitespace>
    All of this
    whitespace   will
    be preserved.
</div>

Alternatively, the <marko-compiler-options> tag may be used to configure whitespace for the entire template:

<marko-compiler-options preserve-whitespace/>
<marko-compiler-options preserve-whitespace/>

marko-body

The marko-body attribute can be used to control how body content is parsed. The following values are supported:

  • html - Body content will be parsed HTML (the default)
  • static-text - Body content will be parsed as static text (HTML tags will be ignored). Placeholders will be ignored.
  • parsed-text - Body content will be parsed as text (HTML tags will be ignored). Placeholders will not be ignored.
input.marko
<div marko-body="static-text">
    This is just one
    <span if(foo)>
            Hello ${THIS IS NOT VALID}!
    </span>
    big text block
</div>
div marko-body="static-text"
  --- 
  This is just one
  <span if(foo)>
          Hello ${THIS IS NOT VALID}!
  </span>
  big text block
  ---
output.html
<div>
    This is just one
    &lt;span if(foo)&gt;
        Hello ${THIS IS NOT VALID}!
    &lt;/span&gt;
    big text block
</div>
EDIT

Contributors

Helpful? You can thank these awesome people! You can also edit this doc if you see any issues or want to improve it.