HTL - bartoszWesolowski/aem-tips GitHub Wiki

HTL

  • separates logic from presentation
  • simple to write and understand

Documentation

Examples

Comparison Operators

  • Comparators works similar to JS comparators == => JS ===, != => JS !==
  • <, >, <=, >= comparators for numbers supported

Comparing enums

${enumConstant == 'CONSTANT_NAME'}     <!-- Java Enum comparison -->

Relational Operations (in)

${'a' in 'abc'} <!--/* returns true */-->
${'ab' in 'abc'} <!--/* returns true */-->
${'bc' in 'abc'} <!--/* returns true */-->
${'abc' in 'abc'} <!--/* returns true */-->
${'d' in 'abc'} <!--/* returns false */-->

<!--/*
  Assuming myArray would be in scope and would have the following content:
  [100, 200, 300, 400, 500]
*/-->
${100 in myArray} <!--/* returns true */-->
${300 in myArray} <!--/* returns true */-->
${1 in myArray} <!--/* returns false */-->

<!--/*
  Assuming logic is a map 
 {
        a: true,
        b: 'two',
        c: 3
 }
*/-->

${'a' in logic} <!--/* returns true */-->
${'b' in logic} <!--/* returns true */-->
${'c' in logic} <!--/* returns true */-->
${'two' in logic} <!--/* returns false */-->

Comments

  • HTL comment will not be included in the HTML output
<!--/* The content of this comment will be removed from the output. */-->
  • HTL expressions inside HTML comments are evaluated, comment content visible in HTML

Format

<!--/* Numbered parameters for injecting variables: */-->
${'Asset {0}' @ format=properties.assetName}   <!--/* Basically a shortcut of the array notation, useful when it has only one element */-->
${'Asset {0}' @ format=[properties.assetName]}
${'Asset {0} out of {1}' @ format=[properties.current, properties.total]}
${'Asset {0} out of {1}' @ format=[properties.current, properties.total], i18n, locale='de'}

output

Asset Night Sky
Asset Night Sky
Asset 3 out of 5
Bild 3 von 5

assuming that

properties.assetName = 'Night Sky'
properties.current   = 3
properties.total     = 5
formatter 'Asset {0} out of {1}' will be translated to 'Bild {0} von {1}' for the 'de' locale by i18n
  • Sets the content of current element - element content is replaced with given value.
  • Value required
  • Basic example
============ Markup ============
<p data-sly-text="Hello world">This text would never be shown.</p>

============ Output ============
<p>Hello world</p>
  • Whole content is replaced with given text
============ Markup ============
<div data-sly-text="Text provided in data-sly-text">
    <p>some text in p element</p>
    <div>some other element</div>
</div>

============ Output ============
<div>
    Text provided in data-sly-text
</div>
  • If value is empty it will display given tag with no content
============ Markup ============
<div data-sly-text="${null}">
  <p>data-sly-text with null value</p>
  <div>this will not be rendered</div>
</div>

============ Output ============
<div></div>
  • Other examples from documentation
<p data-sly-text="${''}"></p>    <!--/* outputs: */--> <p></p>
<p data-sly-text="${[]}"></p>    <!--/* outputs: */--> <p></p>
<p data-sly-text="${0}"></p>     <!--/* outputs: */--> <p>0</p>
<p data-sly-text="${false}"></p> <!--/* outputs: */--> <p>false</p>
  • By default context is set to text but it can be changed
============ Markup ============
<p data-sly-text="${'<strong>Bold and Proud</strong>' @ context='html'}"></p>

============ Output ============
<strong>Bold and Proud</strong>

Without context change the default one is used and text is escaped:

============ Markup ============
<p data-sly-text="${'<strong>Bold and Proud</strong>'}"></p>

============ Output ============
&lt;strong&gt;Bold and Proud&lt;/strong&gt;
  • Sets an attribute or a group of attributes on the current element.
  • Attribute value: optional; String for setting attribute content, or Boolean for setting boolean attributes, or Object for setting multiple attributes; removes the attribute if the value is omitted.
  • Attribute identifier: optional; the attribute name; must be omitted only if attribute value is an Object.
  • Attributes are processed left to right (last value will be used in case multiple attributes with same name are present)
<!--/* given object
    foobar = {'id': 'foo', 'class': 'bar', 'lang': ''}
*/-->

============ Markup ============
<div class="bar1" data-sly-attribute.class="bar2" data-sly-attribute="${foobar}"></div>
============ Output ============
<div id="foo" class="bar"></div>
 
============ Markup ============
<div data-sly-attribute="${foobar}" data-sly-attribute.class="bar2" id="foo2"></div>
============ Output ============
<div id="foo2" class="bar2"></div>
  • Last attr is always counted - even if it does not use data-sly-attribute directive
============ Markup ============
<p data-sly-attribute.title="Title 1" data-sly-attribute.title="${'Title evaluated'}"
         data-sly-attribute.title="${null}" title="Title plain text">
============ Output ============
<p title="Title plain text"></p>
  • Empty attribute with plain value (no HTL expression inside value) will be rendered
============ Markup ============
<p class="" data-sly-attribute.id=""></p>
============ Output ============
<p class=""></p>
  • I case the last attribute value is empty then no attribute will be rendered
<div lang="${''}"></div>
<div lang="en" data-sly-attribute.lang></div>
<div lang="en" data-sly-attribute.lang=""></div>
<div lang="en" data-sly-attribute.lang="${''}"></div>

============ All of the above output ============
<!--/* All of the above output: */-->
<div></div>
  • Empty attributes are left if no data-sly-attribute is used
============ Markup ============
<div title="" data-sly-attribute="${foobar}"></div>

============ Output ============
<div title="" id="foo" class="bar"></div>
  • boolean atributes are supported
<input checked="${true}"/>
<input data-sly-attribute.checked="${true}"/>
<!--/* Both output: */-->
<input checked/>
 
<input checked="${false}"/>
<input data-sly-attribute.checked="${false}"/>
<!--/* Both output: */-->
<input/>
 
<!--/* But 'true' or 'false' strings don't work the same way: */-->
<input checked="${'true'}"/>  <!--/* outputs: */--> <input checked="true"/>
<input checked="${'false'}"/> <!--/* outputs: */--> <input checked="false"/>
 
<!--/* Consider having attrs={'checked': true} */-->
<input data-sly-attribute="${attrs}"/>
<!--/* outputs: */-->
<input checked/>
  • arrays
<div title="${['one', 'two', 'three']}"></div>
<!--/* outputs: */-->
<div title="one,two,three"></div>
 
<!--/* Like empty strings, empty arrays remove the attribute: */-->
<div title="${[]}"></div>
<!--/* outputs: */-->
<div></div>
 
<!--/* But an array containing just an empty string doesn't get removed: */-->
<div title="${['']}"></div>
<!--/* outputs: */-->
<div title=""></div>
  • numbers are cast as strings
<div class="${0}"></div>
<!--/* outputs: */-->
<div class="0"></div>
  • changes the element tag
<div data-sly-element="${'h1'}">Blah</div>
<div data-sly-element="h1">Blah</div>

============ Both Outputs ============
<h1>Blah</h1>
  • given element has no value
<div data-sly-element="${''}">Blah</div>
<div data-sly-element="">Blah</div>

============ Both Outputs ============
<div >Blah</div>
  • value uses context elementName with supported values: section, nav, article, aside, h1, h2, h3, h4, h5, h6, header, footer, address, main, p, pre, blockquote, ol, li, dl, dt, dd, figure, figcaption, div, a, em, strong, small, s, cite, q, dfn, abbr, data, time, code, var, samp, kbd, sub, sup, i, b, u, mark, ruby, rt, rp, bdi, bdo, span, br, wbr, ins, del, table, caption, colgroup, col, tbody, thead, tfoot, tr, td, th
  • not supported values: <script>, <style>, <form>, or <input>
    <div data-sly-element="input">
      some text
    </div>
    <div data-sly-element="script">
      some text
    </div>
============ Both Outputs ============
<div >some text</div>
  • keeps or removes element depending on value
  • content shown if evaluated to true
<p data-sly-test.editOrDesign="${wcmmode.edit || wcmmode.design}">displays the content when in `edit` or `design` mode</p>
<p data-sly-test="${!editOrDesign && pageProperties.jcr:title}">displays the content when in `edit` or `design` mode and the `pageProperties` contain a non-empty `jcr:title` property</p>
  • value evaluated as boolean but not cased when variable is defined
<p data-sly-test.myVar="${'foo'}">${myVar}</p>
<!--/* outputs: */-->
<p>foo</p>
  • multiple test with same variable name (values evaluated from top to bottom)
    <div data-sly-test.variable="${'variable in outer div'}">
      Variable value="${variable}"
      <p data-sly-test.variable="${'variable first div in inner p'}">Variable value in first div in p="${variable}"</p>
    </div>
    <div data-sly-test.variable="${'variable in second div'}">
      Variable value in div sibling="${variable}"
      <p>
        Variable value in div sibling child p="${variable}"
      </p>
    </div>

============ Output ============

    <div>
      Variable value="variable in outer div"
      <p>Variable value in first div in p="variable first div in inner p"</p>
    </div>
    <div>
      Variable value in div sibling="variable in second div"
      <p>
        Variable value in div sibling child p="variable in second div"
      </p>
    </div>
  • zero and empty array evaluates as false and will not render element
<div data-sly-test="${0}">Test 0 will NOT show the element</div>
<div data-sly-test="${'   '}">Blank string evaluates to false</div>
<div data-sly-test="${[]}">Test empty array will NOT show the element</div>
  • any other number or array containing some elements will evaluate to true
<div data-sly-test="${1}">Element will be visible</div>
<div data-sly-test="${-1}">Element will be visible</div>
<div data-sly-test="${['']}">Array with some elements - will be visible</div>
<div data-sly-test="${[0]}">Array with some elements - will be visible</div>

  • Iterates over the content of each item in the attribute value, allowing to control the iteration through the following options:

    • begin - iteration begins at the item located at the specified index; first item of the collection has index 0
    • step - iteration will only process every step items of the collection, starting with the first one
    • end - iteration ends at the item located at the specified index (inclusive)
  • If no value present no element will be shown

  • Usage data-sly-list="${list}" which is equivalent of data-sly-list.item="${list}"

  • Iteration metadata available in object itemList or <variable>List if named variable used, for example data-sly-list.element="${list}" will use elementList iteration object with variables:

    • index: zero-based counter (0..length-1);
    • count: one-based counter (1..length);
    • first: true for the first element being iterated;
    • middle: true if element being iterated is neither the first nor the last;
    • last: true for the last element being iterated;
    • odd: true if count is odd;
    • even: true if count is even.
  • given list numbers = [a, b, c, d, e]

============ Markup ============
<ul data-sly-list.number="${numbers}">
   <li>${number}</li>
</ul>
============ Output ============
<ul>
   <li>a</li>
   <li>b</li>
   ...
   <li>e</li>
</ul>
  • using begin and end parameters
============ Markup ============
<!--/* Iteration control; start from the beginning, stop after the first 3 elements (index 2) */-->
<ul data-sly-list.number="${numbers @ begin = 0, end = 2}}">
   <li>${number}</li>
</ul>
============ Output ============
<ul>
   <li>a</li>
   <li>b</li>
   <li>c</li>
</ul>
  • using begin and end parameters when begin is grater than number of elements
============ Markup ============
<ul data-sly-list.number="${numbers @ begin = 5, end = 10}}">
   <li>${number}</li>
</ul>
============ Output ============
<ul>
</ul>
  • Iterating over map uses entry key for iteration object
<dl data-sly-list="${myMap}">
    <dt>key: ${item}</dt>
    <dd>value: ${myMap[item]}</dd>
</dl>

data-sly-repeat

  • Works in a same way that data-sly-list but repeats current element not element content (child nodes)
  • given list numbers = [a, b, c, d, e]
============ Markup ============
<ul>
   <li data-sly-repeat.number="${numbers}">${number}</li>
</ul>
============ Output ============
<ul>
   <li>a</li>
   <li>b</li>
   ...
   <li>e</li>
</ul>
  • Includes the output of a rendering script run with the current context.
  • <div data-sly-include="template.html"></div>
  • Only content of the included script is rendered
<!--/* template.html: */-->
<h1>Template html</h1>

============ Markup ============
<div class="include" data-sly-include="template.html"/></div>

============ Output============
<h1>Template html</h1>
  • Parameters: appendPath and prependPath:
<div data-sly-include="${'partials' @ appendPath='template.html'}"></div>
<!--/* will include partials/template.html */-->

<div data-sly-include="${'template.html' @ prependPath='partials'}"></div>
<!--/* will include partials/template.html */-->

<div data-sly-include="${'components' @ prependPath='partials', appendPath='template.html'}"></div>
<!--/* will include partials/components/template.html */-->
⚠️ **GitHub.com Fallback** ⚠️