Tuesday, August 9, 2016

HTL (Sightly) Chit Note - Part 1

In this article, I am going to create a chit note for frequently used HTL syntax that will come handy while working with AEM.

1. HTL does not support JSP tag libraries.


2. Including a script: data-sly-include instead of cq:include

<cq:include script="template.html"/> //Including a HTL file in JSP
<sly data-sly-include="template.jsp"/> //Including a JSP file in HTL

The element on which a data-sly-include has been set is ignored and not displayed.

3. Including a Resource: data-sly-resource

Includes the result of rendering the indicated resource through the sling resolution and rendering process. Basic syntax-

<article data-sly-resource="path/to/resource"></article>

Override resourceType of an include-
<article data-sly-resource="${'path/to/resource' @ resourceType='my/resource/type'}"></article>

Change the wcmmode-
<article data-sly-resource="${'path/to/resource' @ wcmmode='disabled'}"></article>

Include resource parent div


<sly data-sly-resource="${'btn1' @ resourceType='my/components/content/button', decoration=true}"></sly>

4. Changing element tag: data-sly-element

This replaces the element name of the host element-
<h1 data-sly-element="${titleLevel}">text</h1>

Replaces the h1 with the value of titleLevel.

For security reasons, data-sly-element accepts only limited element names.

5. Including clientlibs: data-sly-use and data-sly-call 

In a single statement-

<sly data-sly-use.clientlib="/libs/granite/sightly/templates/clientlib.html"
     data-sly-call="${clientlib.all @ categories=['myCategory1', 'myCategory2']}"/>

In Separate statements-

<!doctype html>
<html data-sly-use.clientlib="/libs/granite/sightly/templates/clientlib.html">
    <head>
        <!-- HTML meta-data -->
        <css data-sly-call="${clientlib.css @ categories='myCategory'}"/>
    </head>
    <body>
        <!-- page content -->
        <js data-sly-call="${clientlib.js @ categories='myCategory'}"/>
    </body>
</html>


6. If Statement: data-sly-test

HTL uses data-sly-test block statement to implement "if" behavior. There is no direct if-else implementation. You need to utilize data-sly-test in an efficient manner to achieve this.

A). Positive condition: if
<h1 data-sly-test="${properties.jcr:title}"> ${properties.jcr:title}</h1>
Negative condition: else
<h1 data-sly-test="${!properties.jcr:title}"> ${pageProperties.name | "Untitled" }</h1>

You can also use relational operators i.e.- "<=", ">=", "==" etc. in your test condition.

B). Always cache test block statement results in an identifier if it repeats itself

<h1 data-sly-test.hasTitle="${properties.jcr:title}"> ${properties.jcr:title}</h1> //if
<h1 data-sly-test="${!hasTitle}"> ${pageProperties.name | "Untitled" }</h1> //else

7. data-sly-unwrap/ sly Element

AEM 6.0 (HTL1.0) has an attribute "data-sly-unwrap" to avoid rendering of referenced HTML tag in context. 

<!-- /* This */ -->
<p data-sly-use.nav="navigation.js" data-sly-unwrap>Hello World</p>

<!-- /* Produces */ -->
Hello World

<!-- /* you can also put condition in data-sly-unwrap */ -->

<p data-sly-unwrap="${someCondition}">Hello World</p>

The above statement will render the hosting element only when the condition evaluates to false. 

AEM 6.1 (HTL1.1) has introduced a new element- "sly" to achieve this. So it is suggested to use sly statement instead of data-sly-unwrap in HTL 1.1 onwards.

<sly data-sly-test="${someCondition}">Hello World</sly>

8. Iterating through a list: data-sly-list Element

<ul data-sly-list="${listObject}">
    <li>Index: ${itemList.index}, Value: ${item}</li>
</ul>

Here itemList provides following properties-
  • index : counter (0-n)
  • count : counter (1-n)
  • first : if the current item is the first item
  • middle: if the current item is neither the first nor the last item
  • last: if the current item is the last item
  • odd: if index is odd
  • even: if index is even
You can also rename the item/ itemList variable name (This is suggested approach to use identifiers instead of using default item naming)-

<dl data-sly-list.child="${currentPage.listChildren}">
    <dt>index: ${childList.index}</dt>
    <dd>value: ${child.title}</dd>
</dl>

9. Use of Display Context

To protect against cross-site scripting (XSS) vulnerabilities, HTL automatically recognises the context within which an output string is to be displayed within the final HTML output, and escapes that string appropriately.

<p style="color: ${properties.color @ context='styleToken'};"></p>
<p>URL: ${teaser.link @ context = 'uri'}</p>

${properties.jcr:title @ context='html'}    <!--/* Use for HTML output. Removes markup posing XSS risks */-->
${properties.jcr:title @ context='text'}     <!--/* Use for plain text content - Encodes all HTML */-->
${properties.jcr:title @ context='elementName'}   <!--/* Allows only white-listed element names, outputs 'div' otherwise */-->
${properties.jcr:title @ context='attributeName'} <!--/* Outputs nothing if the value doesn't map to a valid HTML attribute; Doesn't allow 'style' and 'on*' attributes */-->
${properties.jcr:title @ context='attribute'}     <!--/* Applies HTML attribute escaping */-->
${properties.jcr:title @ context='uri'}     <!--/* Outputs nothing if the value contains XSS risks */-->
${properties.jcr:title @ context='scriptToken'}   <!--/* Outputs nothing if value doesn't correspond to the JavaScript token syntax */-->
${properties.jcr:title @ context='scriptString'}  <!--/* Applies JavaScript string escaping */-->
${properties.jcr:title @ context='scriptComment'} <!--/* Context for Javascript block comments. Outputs nothing if value break out of the comment context */-->
${properties.jcr:title @ context='scriptRegExp'}  <!--/* Applies JavaScript Regex escaping */-->
${properties.jcr:title @ context='styleToken'}    <!--/* Outputs nothing if the value doesn't correspond to the CSS token syntax */-->
${properties.jcr:title @ context='styleString'}   <!--/* Applies CSS string escaping */-->
${properties.jcr:title @ context='styleComment'}  <!--/* Context for CSS comments. Outputs nothing if value break out of the comment context */-->
${properties.jcr:title @ context='comment'}       <!--/* Applies HTML comment escaping */-->
${properties.jcr:title @ context='number'}        <!--/* Outputs zero if the value is not a number */-->
${properties.jcr:title @ context='unsafe'}  <!--/* Use at your risk, this disables XSS protection */-->

10. HTL Comments:

<!--/* An HTL Comment */-->


11. Formatting in Expression:

To format string, you can use Numbered parameters for injecting variables:

${'Assets {0}' @ format=properties.assetName}   <!--/* A shortcut of the array notation, useful when it has one element */-->
${'Assets {0}' @ format=[properties.assetName]}
${'Assets {0} - {1} of {2}' @ format=[properties.first, properties.last, properties.total]}

12. i18n: Internationalization

Following syntax is used to localize string literals in HTL-

${'Assets' @ i18n} <!--/* Translates the string to the resource language */-->

${'Assets' @ i18n, locale='en-US', hint='Translation Hint'} <!--/* Complete syntax with locale and hint options */-->

The i18n option can be combined with the format option, which replaces the placeholders after the string has been run through the dictionary:

${'Assets {0} - {1} of {2}' @ i18n, format=[properties.first, properties.last, properties.total]}

13. Array Join: Implode function

The join option allows to control the output of an array object by specifying the separator string.

${['one', 'two'] @ join='; '} <!--/* outputs: one; two */-->

14. URI Manipulation

Following options are available-
  • scheme (To prepend with http/ https)
    ${'http://example.com/path/page.html' @ scheme='https'} <!-- outputs: https://example.com/path/page.html -->
  • domain
    ${'///path/page.html' @ domain='example.org'} <!-- outputs: //example.org/path/page.html -->
  • path
    ${'http://example.com/this/one.selector.html/suffix?key=value#fragment' @ path=''} <!-- outputs: http://example.com/this/one.selector.html/suffix?key=value#fragment -->
  • prependPath
  • appendPath
    ${'path/page.selector.html/suffix?key=value#fragment' @ appendPath='appended'} <!-- outputs: path/page/appended.selector.html/suffix?key=value#fragment -->
  • selectors
  • addSelectors
  • removeSelectors
  • extension
    ${'path/page.json#fragment' @ extension='html'} <!-- outputs: path/page.html#fragment -->
  • suffix
    ${'path/page.html?key=value' @ suffix='my/suffix'} <!-- outputs: path/page.html/my/suffix?key=value -->
  • prependSuffix
  • appendSuffix
  • query
    ${'http://www.example.org/search?s=1&q=htl' @ query} <!-- outputs: http://www.example.org/search -->
  • addQuery
  • removeQuery
  • fragment
    ${'path/page#one' @ fragment='two'} <!-- outputs: path/page#two -->


References:

2 comments:

  1. How can I use a multi-field in HTL without writing a back-end java class ? Can we iterate it like we used to do in old jsps using properties.get ? I am looking for a way to use multi-field's values in HTL's .html without having to write a back-end java class. Please let me know.

    ReplyDelete
    Replies

    1. Hi Amit,

      It depends on the format of data, you are storing in repo. To get property value use- resource.valueMap.propName Or properties.propName. If it's in array format then you can use data-sly-list or data-sly-repeat to iterate through it. But if the data gets stored in other format e.g. JSON then you need to use Java or JavaScript to render it. If you are storing field values as multiple nodes under a parent then you can use following code-

      #sly data-sly-list="${properties.linklist}"#
      #sly data-sly-use.itemnode="${'/etc/designs/myProject/clientlibs/authoring/custom.js' @ fieldValue =item}"#
      ${itemnode.listJson.propertyName}
      #/sly#
      #/sly#

      Replace # with < or > as required.

      Content for custom.js file--

      "use strict";

      use(function () {

      var itemJson = JSON.parse(this.fieldValue);

      return {
      listJson: itemJson
      }
      });

      Delete

CDN | Clearing Cloudflare cache

In order to clear Cloudflare cache automatically via code, follow below steps: 1. Develop Custom TransportHandler Develop a custom Trans...