Permalink
Switch branches/tags
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
521 lines (384 sloc) 11.2 KB

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>

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>

And support complex expressions:

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

<for>

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

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

It may also be applied as an attribute:

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

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>
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>

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(i from 0 to 10)>
        ${i}
    </li>
</ul>
<ul>
    <li for(i from 0 to 10 step 2)>
        ${i}
    </li>
</ul>
<ul>
    <li for(i from 0 to myArray.length-1)>
        ${myArray[i]}
    </li>
</ul>

Property Looping

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

Native JavaScript for-loop

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

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>

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>

Applied as an element:

$ var n = 0;

<ul>
    <while(n < 4)>
        <li>${n++}</li>
    </while>
</ul>

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>

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>

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

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(2, 3)}</div>

<${dynamic}>

<${useDiv ? 'div' : 'span'}>
    Hello there!
</>

The <${dynamic}> tag is used to render a tag, component, or transcluded content that isn't determined until runtime.

Dynamic components

The <${dynamic}> tag can be used to embed another template in the current template:

import componentA from "./path/to/component-a.marko";
import componentB from "./path/to/component-b.marko";

<${useA ? componentA : componentB} name="Frank"/>

You can also switch between a normal HTML tag and a Marko component:

import FancyButton from "./path/to/fancy-button.marko";

<${isFancy ? FancyButton : 'button'}>
    Button text
</>

Layouts and transcluded content

You can pass transcluded content to a tag using nested attribute tags which are denoted by the @ symbol:

page.marko

<layout>
    <@body>
        <h1>Hello Marko</h1>
    </@body>
</layout>

Then in your layout template you can render the transcluded content using the <${dynamic}> tag:

layout.marko

<!doctype html>
<html>
    <body>
        <!-- this comes from <@body> -->
        <${input.body}/>
    </body>
</html>

<include-text>

<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')/>

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>

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>

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>

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>
// 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>

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/>

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>

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

<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>

output.html

<div>
  This is just one &lt;span if(foo)&gt; Hello ${THIS IS NOT VALID}!
  &lt;/span&gt; big text block
</div>