Thursday, 11 August 2016

HTL (Sightly) Code Snippets


Below are few HTL syntax used frequently in 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>



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 -->
Use hashmap in sightly / HTL

WCMUsePojo code

package com.kishore.sightly;

public class GetMap extends WCMUsePojo {
    Map<String, String> myMap = null; 
    @Override
    public void activate() throws Exception {
        myMap = new HashMap<String, String>();
        myMap.put("name", "Kishore");
    }

    public Map<String, String> getmyMap() {
            return myMap;
    }
}

HTL code



<sly data-sly-use.model="com.kishore.sightly.GetMap" />

Name: ${model.myMap['name']}

4 comments :

  1. Which display context should be used to display the keyword that I put in the Search box?

    ReplyDelete
  2. Which display context should be used to display the keyword that I put in the Search box?

    ReplyDelete
  3. Wow, this is really interesting reading. I am glad I found this and got to read it. Great job on this content. I like it. CouponMonkey

    ReplyDelete