Formerly Known As “Sightly”
HTML Template Language (also referred to as HTL) is Adobe Experience Manager’s preferred and recommended server-side template system for HTML. As in all HTML server-side templating systems, a HTL file defines the output sent to the browser by specifying the HTML itself, some basic presentation logic and variables to be evaluated at runtime.
HTML Template Language (also referred to as HTL) is Adobe Experience Manager’s preferred and recommended server-side template system for HTML. As in all HTML server-side templating systems, a HTL file defines the output sent to the browser by specifying the HTML itself, some basic presentation logic and variables to be evaluated at runtime.
Advantages
- HTL is capable of automatically applying the proper context-aware escaping to all variables being output to the presentation layer(expressions placed in href or src attributes to be escaped differently from expressions placed in other attributes).
- always enforcing a strict separation of concerns between markup and logic.
- allow HTML developers without Java knowledge and with little product-specific knowledge to be able to edit HTL templates, allowing them to be part of the development team
HTL over JSP
It is recommended for new AEM projects to use the HTML Template Language, as it offers multiple benefits compared to JSP. For existing projects though, a migration only makes sense if it is estimated to be less effort than maintaining the existing JSPs for the coming years.
Even within the same component, HTL files can be used alongside JSPs and ESPs. Following example shows on line 1 how to include an HTL file from a JSP file, and on line 2 how a JSP file can be included from an HTL file:
<cq:include script="template.html"/>
<sly data-sly-include="template.jsp"/>
Fundamental Concepts of HTL
The HTML Template Language uses an expression language to insert pieces of content into the rendered markup, and HTML5 data attributes to define statements over blocks of markup (like conditions or iterations). As HTL gets compiled into Java Servlets, the expressions and the HTL data attributes are both evaluated entirely server-side, and nothing remains visible in the resulting HTML.
Blocks and Expressions
Here’s a first example, which could be contained as is in a template.html file:
<h1 data-sly-test="${properties.jcr:title}">
${properties.jcr:title}
</h1>
Two different kind of syntaxes can be distinguished:
Note:
The SLY element has been introduced with AEM 6.1, or HTL 1.1.
Prior to that, the data-sly-unwrap attribute had to be used instead.
Sometimes there might not be an existing element at the exact location where a block statement has to be inserted. For such cases, it is possible to insert a special SLY element that will be automatically removed from the output, while executing the attached block statements and displaying its content accordingly.
data-sly-use with resources
This allows to get resources directly in HTL with data-sly-use and does not require to write code to get the resource.
For example:
<div data-sly-use.product=“/etc/commerce/product/12345”>
${ product.title }
</div>
Request-attributes
In the data-sly-include and data-sly-resource you can now pass requestAttributes in order to use them in the receiving HTL-script.
This allows you to properly pass-in parameters into scripts or components.
<sly data-sly-use.settings="com.adobe.examples.htl.core.hashmap.Settings"
data-sly-include="${ 'productdetails.html' @ requestAttributes=settings.settings}" />
Java-code of the Settings class, the Map is used to pass in the requestAttributes:
public class Settings extends WCMUsePojo {
// used to pass is requestAttributes to data-sly-resource
public Map<String, Object> settings = new HashMap<String, Object>();
@Override
public void activate() throws Exception {
settings.put("layout", "flex");
}
}
For example, via a Sling-Model, you can consume the value of the specified requestAttributes.
In this example, layout is injected via the Map from the Use-class:
@Model(adaptables=SlingHttpServletRequest.class)
public class ProductSettings {
@Inject @Optional @Default(values="empty")
public String layout;
}
Fix for @extension
The @extension works in all scenarios in AEM 6.3, before you could have a result like http://www.adobe.com.html and also checks whether to add or not add the extension.
${ link @ extension = 'html' }
HTL Global Objects
Without having to specify anything, HTL provides access to all objects that were commonly available in JSP after including global.jsp. These objects are in addition to any that may be introduced through the Use-API.
Enumerable Objects
These objects provide convenient access to commonly used information. Their content can be accessed with the dot notation, and they can be iterated-through using data-sly-list or data-sly-repeat.
Java-backed Objects
Each of the following objects is backed by the corresponding Java object.
The most useful variables in the table below are highlighted in bold.
JavaScript-backed Objects
There are also objects available that are backed by JavaScript. However, as of AEM 6.2 these objects are still experimental and it is better to use the Java-backed objects, which allow to do the same.
<cq:include script="template.html"/>
<sly data-sly-include="template.jsp"/>
Fundamental Concepts of HTL
The HTML Template Language uses an expression language to insert pieces of content into the rendered markup, and HTML5 data attributes to define statements over blocks of markup (like conditions or iterations). As HTL gets compiled into Java Servlets, the expressions and the HTL data attributes are both evaluated entirely server-side, and nothing remains visible in the resulting HTML.
Blocks and Expressions
Here’s a first example, which could be contained as is in a template.html file:
<h1 data-sly-test="${properties.jcr:title}">
${properties.jcr:title}
</h1>
Two different kind of syntaxes can be distinguished:
- Block Statements
- To conditionally display the <h1> element, a data-sly-test HTML5 data attribute is used. HTL provides multiple such attributes, which allow to attach behavior to any HTML element, and all are prefixed with data-sly.
- Expression Language
- HTL expressions are delimited by characters ${ and }. At runtime, these expressions are evaluated and their value is injected into the outgoing HTML stream.
Note:
The SLY element has been introduced with AEM 6.1, or HTL 1.1.
Prior to that, the data-sly-unwrap attribute had to be used instead.
Sometimes there might not be an existing element at the exact location where a block statement has to be inserted. For such cases, it is possible to insert a special SLY element that will be automatically removed from the output, while executing the attached block statements and displaying its content accordingly.
So following example:
<sly data-sly-test="${properties.jcr:title && properties.jcr:description}">
<h1>${properties.jcr:title}</h1>
<p>${properties.jcr:description}</p>
</sly>
will output something like following HTML, but only if there are both, a jcr:title and a jcr:decription property defined, and if none of them are empty:
<h1>MY TITLE</h1>
<p>MY DESCRIPTION</p>
One thing to take care though is to only use the SLY element when no existing element could have been annotated with the block statement, because SLY elements deter the value offered by the language to not alter the static HTML when making it dynamic.
For example, if the previous example would have been wrapped already inside a DIV element, then the added SLY element would be abusive:
<div>
<sly data-sly-test="${properties.jcr:title && properties.jcr:description}">
<h1>${properties.jcr:title}</h1>
<p>${properties.jcr:description}</p>
</sly>
</div>
and the DIV element could have been annotated with the condition:
<div data-sly-test="${properties.jcr:title && properties.jcr:description}">
<h1>${properties.jcr:title}</h1>
<p>${properties.jcr:description}</p>
</div>
HTL Comments
Following example shows on line 1 an HTL comment, and on line 2 an HTML comment:
<!--/* An HTL Comment */-->
<!-- An HTML Comment -->
HTML comments cannot contain HTL comments and vice versa.
Special Contexts
Element and Attribute Names
Expressions can only be placed into HTML text or attribute values, but not within element names or attribute names, or it wouldn’t be valid HTML anymore. In order to set element names dynamically, the data-sly-element statement can be used on the desired elements, and to dynamically set attribute names, even setting multiple attributes at once, the data-sly-attribute statement can be used.
<h1 data-sly-element="${myElementName}" data-sly-attribute="${myAttributeMap}">...</h1>
Contexts Without Block Statements
As HTL uses data attributes to define block statements, it is not possible to define such block statements inside of following contexts, and only expressions can be used there:
Following example illustrates the behavior for HTML comments, but in script or style elements, the same behavior would be observed:
<!--
The title is: ${properties.jcr:title}
<h1 data-sly-test="${properties.jcr:title}">${properties.jcr:title}</h1>
-->
will output something like following HTML:
<!--
The title is: MY TITLE
<h1 data-sly-test="MY TITLE">MY TITLE</h1>
-->
Explicit Contexts Required
One objective of HTL is to reduce the risks of introducing cross-site scripting (XSS) vulnerabilities by automatically applying context-aware escaping to all expressions. While HTL can automatically detect the context of expressions placed inside of HTML markup, it doesn’t analyze the syntax of inline JavaScript or CSS, and therefore relies on the developer to specify explicitly what exact context has to be applied to such expressions.
<sly data-sly-test="${properties.jcr:title && properties.jcr:description}">
<h1>${properties.jcr:title}</h1>
<p>${properties.jcr:description}</p>
</sly>
will output something like following HTML, but only if there are both, a jcr:title and a jcr:decription property defined, and if none of them are empty:
<h1>MY TITLE</h1>
<p>MY DESCRIPTION</p>
One thing to take care though is to only use the SLY element when no existing element could have been annotated with the block statement, because SLY elements deter the value offered by the language to not alter the static HTML when making it dynamic.
For example, if the previous example would have been wrapped already inside a DIV element, then the added SLY element would be abusive:
<div>
<sly data-sly-test="${properties.jcr:title && properties.jcr:description}">
<h1>${properties.jcr:title}</h1>
<p>${properties.jcr:description}</p>
</sly>
</div>
and the DIV element could have been annotated with the condition:
<div data-sly-test="${properties.jcr:title && properties.jcr:description}">
<h1>${properties.jcr:title}</h1>
<p>${properties.jcr:description}</p>
</div>
HTL Comments
Following example shows on line 1 an HTL comment, and on line 2 an HTML comment:
<!--/* An HTL Comment */-->
<!-- An HTML Comment -->
HTML comments cannot contain HTL comments and vice versa.
Special Contexts
Element and Attribute Names
Expressions can only be placed into HTML text or attribute values, but not within element names or attribute names, or it wouldn’t be valid HTML anymore. In order to set element names dynamically, the data-sly-element statement can be used on the desired elements, and to dynamically set attribute names, even setting multiple attributes at once, the data-sly-attribute statement can be used.
<h1 data-sly-element="${myElementName}" data-sly-attribute="${myAttributeMap}">...</h1>
Contexts Without Block Statements
As HTL uses data attributes to define block statements, it is not possible to define such block statements inside of following contexts, and only expressions can be used there:
- HTML comments
- Script elements
- Style elements
Following example illustrates the behavior for HTML comments, but in script or style elements, the same behavior would be observed:
<!--
The title is: ${properties.jcr:title}
<h1 data-sly-test="${properties.jcr:title}">${properties.jcr:title}</h1>
-->
will output something like following HTML:
<!--
The title is: MY TITLE
<h1 data-sly-test="MY TITLE">MY TITLE</h1>
-->
Explicit Contexts Required
One objective of HTL is to reduce the risks of introducing cross-site scripting (XSS) vulnerabilities by automatically applying context-aware escaping to all expressions. While HTL can automatically detect the context of expressions placed inside of HTML markup, it doesn’t analyze the syntax of inline JavaScript or CSS, and therefore relies on the developer to specify explicitly what exact context has to be applied to such expressions.
Since not applying the correct escaping results in XSS vulnerabilities, HTL does therefore remove the output of all expressions that are in script and style contexts when the context has not been declared.
Here is an example of how to set the context for expressions placed inside scripts and styles:
<script> var trackingID = "${myTrackingID @ context='scriptString'}"; </script>
<style> a { font-family: "${myFont @ context='styleString'}"; } </style>
General Capabilities of HTL
This section quickly walks through the general features of the HTML Template Language.
Use-API for Accessing Logic
Consider following example:
<p data-sly-use.logic="logic.js">${logic.title}</p>
And following logic.js server-side executed JavaScript file placed next to it:
use(function () {
return {
title: currentPage.getTitle().substring(0, 10) + "..."
};
});
Automatic Context-Aware Escaping
Consider following example:
<p data-sly-use.logic="logic.js">
<a href="${logic.link}" title="${logic.title}">
${logic.text}
</a>
</p>
Assuming following logic.js file:
use(function () {
return {
link: "#my link's safe",
title: "my title's safe",
text: "my text's safe"
};
});
The initial example will then result in following output:
<p>
<a href="#my%20link%27s%20safe" title="my title's safe">
my text's safe
</a>
</p>
Automatic Removal of Empty Attributes
Consider following example:
<p class="${properties.class}">some text</p>
If the value of the class property happens to be empty, the HTML Template Language will automatically remove the entire class attribute from the output.
<script> var trackingID = "${myTrackingID @ context='scriptString'}"; </script>
<style> a { font-family: "${myFont @ context='styleString'}"; } </style>
General Capabilities of HTL
This section quickly walks through the general features of the HTML Template Language.
Use-API for Accessing Logic
Consider following example:
<p data-sly-use.logic="logic.js">${logic.title}</p>
And following logic.js server-side executed JavaScript file placed next to it:
use(function () {
return {
title: currentPage.getTitle().substring(0, 10) + "..."
};
});
Automatic Context-Aware Escaping
Consider following example:
<p data-sly-use.logic="logic.js">
<a href="${logic.link}" title="${logic.title}">
${logic.text}
</a>
</p>
Assuming following logic.js file:
use(function () {
return {
link: "#my link's safe",
title: "my title's safe",
text: "my text's safe"
};
});
The initial example will then result in following output:
<p>
<a href="#my%20link%27s%20safe" title="my title's safe">
my text's safe
</a>
</p>
Automatic Removal of Empty Attributes
Consider following example:
<p class="${properties.class}">some text</p>
If the value of the class property happens to be empty, the HTML Template Language will automatically remove the entire class attribute from the output.
Additionally, the type of the variable placed in the expression matters:
String:
Boolean:
String:
- not empty: Sets the string as attribute value.
- empty: Removes the attribute altogether.
Boolean:
- true: Displays the attribute without value (as a Boolean HTML attribute).
- false: Removes the attribute altogether.
Here’s an example of how a Boolean expression would allow to control a Boolean HTML attribute:
<input type="checkbox" checked="${properties.isChecked}"/>
Common Patterns with HTL
Loading Client Libraries
In HTL, client libraries are loaded through a helper template provided by AEM, which can be accessed through data-sly-use. Three templates are available in this file, which can be called through data-sly-call:
css – Loads only the CSS files of the referenced client libraries.
js – Loads only the JavaScript files of the referenced client libraries.
all – Loads all the files of the referenced client libraries (both CSS and JavaScript).
<input type="checkbox" checked="${properties.isChecked}"/>
Common Patterns with HTL
Loading Client Libraries
In HTL, client libraries are loaded through a helper template provided by AEM, which can be accessed through data-sly-use. Three templates are available in this file, which can be called through data-sly-call:
css – Loads only the CSS files of the referenced client libraries.
js – Loads only the JavaScript files of the referenced client libraries.
all – Loads all the files of the referenced client libraries (both CSS and JavaScript).
Each helper template expects a categories option for referencing the desired client libraries. That option can be either an array of string values, or a string containing a comma separated values list.
Here are two few short examples:
Loading multiple client libraries fully at once
<sly data-sly-use.clientlib="/libs/granite/sightly/templates/clientlib.html"
data-sly-call="${clientlib.all @ categories=['myCategory1', 'myCategory2']}"/>
Referencing a client library in different sections of a page
<!doctype html>
<html data-sly-use.clientlib="/libs/granite/sightly/templates/clientlib.html">
<head>
<!-- HTML meta-data -->
<sly data-sly-call="${clientlib.css @ categories='myCategory'}"/>
</head>
<body>
<!-- page content -->
<sly data-sly-call="${clientlib.js @ categories='myCategory'}"/>
</body>
</html>
In the second example above, in case the HTML head and body elements are placed into different files, the clientlib.htmltemplate would then have to be loaded in each file that needs it.
Passing Data to the Client
The best and most elegant way to pass data to the client in general, but even more with HTL, is to use data attributes.
Following example shows how the logic (which could also be written in Java) can be used to very conveniently serialize to JSON the object that is to be passed to the client, which can then very easily be placed into a data attribute:
<!--/* template.html file: */-->
<divdata-sly-use.logic="logic.js"data-json="${logic.json}">...</div>
/* logic.js file: */
use(function () {
var myData = {
str: "foo",
arr: [1, 2, 3]
};
return {
json: JSON.stringify(myData)
};
});
From there, it is easy to imagine how a client-side JavaScript can access that attribute and parse again the JSON. This would for instance be the corresponding JavaScript to place into a client library:
var elements = document.querySelectorAll("[data-json]");
for (var i = 0; i < elements.length; i++) {
var obj = JSON.parse(elements[i].dataset.json);
//console.log(obj);
}
HTL Expression Language
The HTML Template Language uses an expression language to access the data structures that provide the dynamic elements of the HTML output. These expressions are delimited by characters ${ and }. To avoid malformed HTML, expressions can only be used in attribute values, in element content, or in comments.
<!-- ${component.path} -->
<h1 class="${component.name}">
${properties.jcr:title}
</h1>
Expressions can be escaped by prepended by a \ character, for instance \${test} will render ${test}.
The expression syntax includes variables, literals, operators and options:
Variables
Without having to specify anything, HTL provides access to all objects that were commonly available in JSP after including global.jsp. The Global Objects page provides the list of all objects provided access to by HTL.
Property Access
There are two ways to access properties of variables, with a dot notation, or with a bracket notation:
${currentPage.title}
${currentPage[‘title’]} or ${currentPage[“title”]}
The simpler dot notation should be preferred for most cases, and the brackets notation should be used to access properties that contain invalid identifier characters, or to access properties dynamically.
The accessed properties can be functions, however passing arguments is not supported, so only functions that don’t expect arguments can accessed, like getters. This is a desired limitation, which is intended to reduce the amount of logic embedded into expressions. If needed, the data-sly-use statement can be used to pass parameters to the logic.
Also shown in the example above is that Java getter functions, like getTitle(), can be accessed without prepending the get, and by lowering the case of the character that follows.
Valid Indentifier Characters
The names of variables, called identifiers, conform to certain rules. They must start with a letter (A–Z and a–z), or an underscore (_), and subsequent characters can also be digits (0–9) or colon (:). Unicode letters such as å and ü cannot be used in identifiers.
Given that the colon (:) character is common in AEM property names, it is convenient that it is a valid identifier character:
${properties.jcr:title}
The bracket notation can be used to access properties that contain invalid identifier characters, like the space character in the example below:
${properties[‘my property’]}
Accessing Members Dynamically
${properties[myVar]}
Permissive Handling of Null Values
${currentPage.lastModified.time.toString}
Options
Expression options can act on the expression and modify it, or serve as parameters when used in conjunction with block statements.
Everything after the @ is an option:
${myVar @ optOne}
Options can have a value, which may be a variable or a literal:
${myVar @ optOne=someVar}
${myVar @ optOne='bar'}
${myVar @ optOne=10}
${myVar @ optOne=true}
Multiple options are separated by commas:
${myVar @ optOne, optTwo=bar}
Parametric expressions containing only options are also possible:
${@ optOne, optTwo=bar}
String Formatting
Option that replaces the enumerated placeholders, {n}, with the corresponding variable:
${'Page {0} of {1}' @ format=[current, total]}
Internationalization
Translates the string to the language of the current source (see below), using the current dictionary. If no translation is found, the original string is used.
${'Page' @ i18n}
The hint option can be used to provide a comment for translators, specifying the context in which the text is used:
${'Page' @ i18n, hint='Translation Hint'}
The default source for the language is ‘resource’, meaning that the text gets translated to the same language as the content. This can be changed to ‘user’, meaning that the language is taken from the browser locale or from the locale of the logged-in user:
${'Page' @ i18n, source='user'}
Providing an explicit locale overrides the source settings:
${'Page' @ i18n, locale='en-US'}
To embed variables into a translated string, the format option can be used:
${'Page {0} of {1}' @ i18n, format=[current, total]}
Array Join
By default, when displaying an array as text, HTL will display comma separated values (without spacing).
Use the join option to specify a different separator:
${['one', 'two'] @ join='; '}
Display Context
The display context of a HTL expression refers to its location within the structure of the HTML page. For example, if the expression appears in place that would produce a text node once rendered, then it is said to be in a text context. If it is found within the value of an attribute, then it is said to be in an attribute context, and so forth.
With the exception of script (JS) and style (CSS) contexts, HTL will automatically detect the context of expressions and escape them appropriately, to prevent XSS security problems. In the case of scripts and CSS, the desired context behavior must be explicitly set. Additionally, the context behavior can also be explicitly set in any other case where an override of the automatic behavior is desired.
Here we have three variables in three different contexts: properties.link (uri context), properties.title(attribute context) and properties.text (text context). HTL will escape each of these differently in accordance with the security requirements of their respective contexts. No explicit context setting is required in normal cases such as this one:
<a href="${properties.link}" title="${properties.title}">${properties.text}</a>
To safely output markup (that is, where the expression itself evaluates to HTML), the html context is used:
<div>${properties.richText @ context='html'}</div>
Explicit context must be set for style contexts:
<span style="color: ${properties.color @ context='styleToken'};">...</span>
Explicit context must be set for script contexts:
<span onclick="${properties.function @ context='scriptToken'}();">...</span>
Escaping and XSS protection can also be turned off:
<div>${myScript @ context='unsafe'}</div>
HTL Block Statements
HTML Template Language (HTL) block statements are custom data attributes added directly to existing HTML. This allows easy and unobtrusive annotation of a prototype static HTML page, converting it to a functioning dynamic template without breaking the validity of the HTML code.
sly element
The <sly> element does not get displayed in the resulting HTML and can be used instead of the data-sly-unwrap. The goal of the <sly> element is to make it more obvious that the element is not outputted. If you want you can still use data-sly-unwrap.
<sly data-sly-test.varone="${properties.yourProp}"/>
As with data-sly-unwrap, try to minimize the use of this.
use
data-sly-use: Initializes a helper object (defined in JavaScript or Java) and exposes it through a variable.
Initialize a JavaScript object, where the source file is located in the same directory as the template. Note that the filename must be used:
<div data-sly-use.nav="navigation.js">${nav.foo}</div>
Initialize a Java class, where the source file is located in the same directory as the template. Note that the classname must be used, not the file name:
<div data-sly-use.nav="Navigation">${nav.foo}</div>
Initialize a Java class, where that class is installed as part of an OSGi bundle. Note that its fully-qualified class name must be used:
<div data-sly-use.nav="org.example.Navigation">${nav.foo}</div>
Parameters can be passed to the initialization using options. Generally this feature should only be used by HTL code that is itself within a data-sly-template block:
<div data-sly-use.nav="${'navigation.js' @parentPage=currentPage}">${nav.foo}</div>
Initialize another HTL template that can then be called using data-sly-call:
<div data-sly-use.nav="navTemplate.html" data-sly-call="${nav.foo}"></div>
unwrap
data-sly-unwrap: Removes the host element from the generated markup while retaining its content. This allows the exclusion of elements that are required as part of HTL presentation logic but are not desired in the actual output.
However, this statement should be used sparingly. In general it is better to keep the HTL markup as close as possible to the intended output markup. In other words, when adding HTL block statements, try as much as possible to simply annotate the existing HTML, without introducing new elements.
For example, this
<p data-sly-use.nav="navigation.js">Hello World</p>
will produce
<p>Hello World</p>
Whereas this,
<p data-sly-use.nav="navigation.js" data-sly-unwrap>Hello World</p>
will produce
Hello World
It is also possible to conditionally unwrap an element:
<div class="popup" data-sly-unwrap="${isPopup}">content</div>
text
data-sly-text: Replaces the content of its host element with the specified text.
For example, this
<p>${properties.jcr:description}</p>
is equivalent to
<p data-sly-text="${properties.jcr:description}">Lorem ipsum</p>
Both will display the value of jcr:description as paragraph text. The advantage of the second method is that is allows the unobtrusive annotation of HTML while keeping the static placeholder content from the original designer.
attribute
data-sly-attribute: Adds attributes to the host element.
For example, this
<div title="${properties.jcr:title}"></div>
is equivalent to
<div title="Lorem Ipsum" data-sly-attribute.title="${properties.jcr:title}"></div>
Both will set the title attribute to the value of jcr:title. The advantage of the second method is that is allows the unobtrusive annotation of HTML while keeping the static placeholder content from the original designer.
Attributes are resolved left to right, with the rightmost instance of an attribute (either literal or defined via data-sly-attribute) taking precedence over any instances of the same attribute (defined either literally or via data-sly-attribute) defined to its left.
Note that an attribute (either literal or set via data-sly-attribute) whose value evaluates to the empty string will be removed in the final markup. The one exception to this rule is that a literal attribute set to a literal empty string will be preserved. For example,
<div class="${''}" data-sly-attribute.id="${''}"></div>
produces,
<div></div>
but,
<div class="" data-sly-attribute.id=""></div>
produces,
<div class=""></div>
To set multiple attributes, pass a map object hold key-value pairs corresponding to the attributes and their values. For example, assuming,
Loading multiple client libraries fully at once
<sly data-sly-use.clientlib="/libs/granite/sightly/templates/clientlib.html"
data-sly-call="${clientlib.all @ categories=['myCategory1', 'myCategory2']}"/>
Referencing a client library in different sections of a page
<!doctype html>
<html data-sly-use.clientlib="/libs/granite/sightly/templates/clientlib.html">
<head>
<!-- HTML meta-data -->
<sly data-sly-call="${clientlib.css @ categories='myCategory'}"/>
</head>
<body>
<!-- page content -->
<sly data-sly-call="${clientlib.js @ categories='myCategory'}"/>
</body>
</html>
In the second example above, in case the HTML head and body elements are placed into different files, the clientlib.htmltemplate would then have to be loaded in each file that needs it.
Passing Data to the Client
The best and most elegant way to pass data to the client in general, but even more with HTL, is to use data attributes.
Following example shows how the logic (which could also be written in Java) can be used to very conveniently serialize to JSON the object that is to be passed to the client, which can then very easily be placed into a data attribute:
<!--/* template.html file: */-->
<divdata-sly-use.logic="logic.js"data-json="${logic.json}">...</div>
/* logic.js file: */
use(function () {
var myData = {
str: "foo",
arr: [1, 2, 3]
};
return {
json: JSON.stringify(myData)
};
});
From there, it is easy to imagine how a client-side JavaScript can access that attribute and parse again the JSON. This would for instance be the corresponding JavaScript to place into a client library:
var elements = document.querySelectorAll("[data-json]");
for (var i = 0; i < elements.length; i++) {
var obj = JSON.parse(elements[i].dataset.json);
//console.log(obj);
}
HTL Expression Language
The HTML Template Language uses an expression language to access the data structures that provide the dynamic elements of the HTML output. These expressions are delimited by characters ${ and }. To avoid malformed HTML, expressions can only be used in attribute values, in element content, or in comments.
<!-- ${component.path} -->
<h1 class="${component.name}">
${properties.jcr:title}
</h1>
Expressions can be escaped by prepended by a \ character, for instance \${test} will render ${test}.
The expression syntax includes variables, literals, operators and options:
Variables
Without having to specify anything, HTL provides access to all objects that were commonly available in JSP after including global.jsp. The Global Objects page provides the list of all objects provided access to by HTL.
Property Access
There are two ways to access properties of variables, with a dot notation, or with a bracket notation:
${currentPage.title}
${currentPage[‘title’]} or ${currentPage[“title”]}
The simpler dot notation should be preferred for most cases, and the brackets notation should be used to access properties that contain invalid identifier characters, or to access properties dynamically.
The accessed properties can be functions, however passing arguments is not supported, so only functions that don’t expect arguments can accessed, like getters. This is a desired limitation, which is intended to reduce the amount of logic embedded into expressions. If needed, the data-sly-use statement can be used to pass parameters to the logic.
Also shown in the example above is that Java getter functions, like getTitle(), can be accessed without prepending the get, and by lowering the case of the character that follows.
Valid Indentifier Characters
The names of variables, called identifiers, conform to certain rules. They must start with a letter (A–Z and a–z), or an underscore (_), and subsequent characters can also be digits (0–9) or colon (:). Unicode letters such as å and ü cannot be used in identifiers.
Given that the colon (:) character is common in AEM property names, it is convenient that it is a valid identifier character:
${properties.jcr:title}
The bracket notation can be used to access properties that contain invalid identifier characters, like the space character in the example below:
${properties[‘my property’]}
Accessing Members Dynamically
${properties[myVar]}
Permissive Handling of Null Values
${currentPage.lastModified.time.toString}
Options
Expression options can act on the expression and modify it, or serve as parameters when used in conjunction with block statements.
Everything after the @ is an option:
${myVar @ optOne}
Options can have a value, which may be a variable or a literal:
${myVar @ optOne=someVar}
${myVar @ optOne='bar'}
${myVar @ optOne=10}
${myVar @ optOne=true}
Multiple options are separated by commas:
${myVar @ optOne, optTwo=bar}
Parametric expressions containing only options are also possible:
${@ optOne, optTwo=bar}
String Formatting
Option that replaces the enumerated placeholders, {n}, with the corresponding variable:
${'Page {0} of {1}' @ format=[current, total]}
Internationalization
Translates the string to the language of the current source (see below), using the current dictionary. If no translation is found, the original string is used.
${'Page' @ i18n}
The hint option can be used to provide a comment for translators, specifying the context in which the text is used:
${'Page' @ i18n, hint='Translation Hint'}
The default source for the language is ‘resource’, meaning that the text gets translated to the same language as the content. This can be changed to ‘user’, meaning that the language is taken from the browser locale or from the locale of the logged-in user:
${'Page' @ i18n, source='user'}
Providing an explicit locale overrides the source settings:
${'Page' @ i18n, locale='en-US'}
To embed variables into a translated string, the format option can be used:
${'Page {0} of {1}' @ i18n, format=[current, total]}
Array Join
By default, when displaying an array as text, HTL will display comma separated values (without spacing).
Use the join option to specify a different separator:
${['one', 'two'] @ join='; '}
Display Context
The display context of a HTL expression refers to its location within the structure of the HTML page. For example, if the expression appears in place that would produce a text node once rendered, then it is said to be in a text context. If it is found within the value of an attribute, then it is said to be in an attribute context, and so forth.
With the exception of script (JS) and style (CSS) contexts, HTL will automatically detect the context of expressions and escape them appropriately, to prevent XSS security problems. In the case of scripts and CSS, the desired context behavior must be explicitly set. Additionally, the context behavior can also be explicitly set in any other case where an override of the automatic behavior is desired.
Here we have three variables in three different contexts: properties.link (uri context), properties.title(attribute context) and properties.text (text context). HTL will escape each of these differently in accordance with the security requirements of their respective contexts. No explicit context setting is required in normal cases such as this one:
<a href="${properties.link}" title="${properties.title}">${properties.text}</a>
To safely output markup (that is, where the expression itself evaluates to HTML), the html context is used:
<div>${properties.richText @ context='html'}</div>
Explicit context must be set for style contexts:
<span style="color: ${properties.color @ context='styleToken'};">...</span>
Explicit context must be set for script contexts:
<span onclick="${properties.function @ context='scriptToken'}();">...</span>
Escaping and XSS protection can also be turned off:
<div>${myScript @ context='unsafe'}</div>
HTL Block Statements
HTML Template Language (HTL) block statements are custom data attributes added directly to existing HTML. This allows easy and unobtrusive annotation of a prototype static HTML page, converting it to a functioning dynamic template without breaking the validity of the HTML code.
sly element
The <sly> element does not get displayed in the resulting HTML and can be used instead of the data-sly-unwrap. The goal of the <sly> element is to make it more obvious that the element is not outputted. If you want you can still use data-sly-unwrap.
<sly data-sly-test.varone="${properties.yourProp}"/>
As with data-sly-unwrap, try to minimize the use of this.
use
data-sly-use: Initializes a helper object (defined in JavaScript or Java) and exposes it through a variable.
Initialize a JavaScript object, where the source file is located in the same directory as the template. Note that the filename must be used:
<div data-sly-use.nav="navigation.js">${nav.foo}</div>
Initialize a Java class, where the source file is located in the same directory as the template. Note that the classname must be used, not the file name:
<div data-sly-use.nav="Navigation">${nav.foo}</div>
Initialize a Java class, where that class is installed as part of an OSGi bundle. Note that its fully-qualified class name must be used:
<div data-sly-use.nav="org.example.Navigation">${nav.foo}</div>
Parameters can be passed to the initialization using options. Generally this feature should only be used by HTL code that is itself within a data-sly-template block:
<div data-sly-use.nav="${'navigation.js' @parentPage=currentPage}">${nav.foo}</div>
Initialize another HTL template that can then be called using data-sly-call:
<div data-sly-use.nav="navTemplate.html" data-sly-call="${nav.foo}"></div>
unwrap
data-sly-unwrap: Removes the host element from the generated markup while retaining its content. This allows the exclusion of elements that are required as part of HTL presentation logic but are not desired in the actual output.
However, this statement should be used sparingly. In general it is better to keep the HTL markup as close as possible to the intended output markup. In other words, when adding HTL block statements, try as much as possible to simply annotate the existing HTML, without introducing new elements.
For example, this
<p data-sly-use.nav="navigation.js">Hello World</p>
will produce
<p>Hello World</p>
Whereas this,
<p data-sly-use.nav="navigation.js" data-sly-unwrap>Hello World</p>
will produce
Hello World
It is also possible to conditionally unwrap an element:
<div class="popup" data-sly-unwrap="${isPopup}">content</div>
text
data-sly-text: Replaces the content of its host element with the specified text.
For example, this
<p>${properties.jcr:description}</p>
is equivalent to
<p data-sly-text="${properties.jcr:description}">Lorem ipsum</p>
Both will display the value of jcr:description as paragraph text. The advantage of the second method is that is allows the unobtrusive annotation of HTML while keeping the static placeholder content from the original designer.
attribute
data-sly-attribute: Adds attributes to the host element.
For example, this
<div title="${properties.jcr:title}"></div>
is equivalent to
<div title="Lorem Ipsum" data-sly-attribute.title="${properties.jcr:title}"></div>
Both will set the title attribute to the value of jcr:title. The advantage of the second method is that is allows the unobtrusive annotation of HTML while keeping the static placeholder content from the original designer.
Attributes are resolved left to right, with the rightmost instance of an attribute (either literal or defined via data-sly-attribute) taking precedence over any instances of the same attribute (defined either literally or via data-sly-attribute) defined to its left.
Note that an attribute (either literal or set via data-sly-attribute) whose value evaluates to the empty string will be removed in the final markup. The one exception to this rule is that a literal attribute set to a literal empty string will be preserved. For example,
<div class="${''}" data-sly-attribute.id="${''}"></div>
produces,
<div></div>
but,
<div class="" data-sly-attribute.id=""></div>
produces,
<div class=""></div>
To set multiple attributes, pass a map object hold key-value pairs corresponding to the attributes and their values. For example, assuming,
attrMap = {
title: "myTitle",
class: "myClass",
id: "myId"
}
Then,
<div data-sly-attribute="${attrMap}"></div>
produces,
<div title="myTitle" class="myClass" id="myId"></div>
element
data-sly-element: Replaces the element name of the host element.
For example,
<h1 data-sly-element="${titleLevel}">text</h1>
Replaces the h1 with the value of titleLevel.
For security reasons, data-sly-element accepts only the following element names:
a abbr address article aside b bdi bdo blockquote br caption cite code col colgroup
data dd del dfn div dl dt em figcaption figure footer h1 h2 h3 h4 h5 h6 header i ins
kbd li main mark nav ol p pre q rp rt ruby s samp section small span strong sub
sup table tbody td tfoot th thead time tr u var wbr
To set other elements, XSS security must be turned off (@context=’unsafe’).
test
data-sly-test: Conditionally removes the host element and it’s content. A value of false removes the element; a value of true retains the element.
For example, the p element and its content will only be rendered if isShown is true:
<p data-sly-test="${isShown}">text</p>
The result of a test can be assigned to a variable that can be used later. This is usually used to construct “if else” logic, since there is no explicit else statement:
<p data-sly-test.abc="${a || b || c}">is true</p>
<p data-sly-test="${!abc}">or not</p>
The variable, once set, has global scope within the HTL file.
Following are some examples on comparing values:
<div data-sly-test="${properties.jcr:title == 'test'}">TEST</div>
<div data-sly-test="${properties.jcr:title != 'test'}">NOT TEST</div>
<div data-sly-test="${properties['jcr:title'].length > 3}">Title is longer than 3</div>
<div data-sly-test="${properties['jcr:title'].length >= 0}">Title is longer or equal to zero </div>
<div data-sly-test="${properties['jcr:title'].length > aemComponent.MAX_LENGTH}">
Title is longer than the limit of ${aemComponent.MAX_LENGTH}
</div>
repeat
With data-sly-repeat you can repeat an element multiple times based on the list that is specified.
<div data-sly-repeat="${currentPage.listChildren}">${item.name}</div>
This works the same way as data-sly-list, except that you do not need a container element.
The following example shows that you can also refer to the item for attributes:
<div data-sly-repeat="${currentPage.listChildren}" data-sly-attribute.class="${item.name}">${item.name}</div>
list
data-sly-list: Repeats the content of the host element for each enumerable property in the provided object.
Here is a simple loop:
<dl data-sly-list="${currentPage.listChildren}">
<dt>index: ${itemList.index}</dt>
<dd>value: ${item.title}</dd>
</dl>
The following default variables are available within the scope of the list:
item: The current item in the iteration.
itemList: Object holding the following properties:
index: zero-based counter (0..length-1).
count: one-based counter (1..length).
first: true if the current item is the first item.
middle: true if the current item is neither the first nor the last item.
last: true if the current item is the last item.
odd: true if index is odd.
even: true if index is even.
Defining an identifier on the data-sly-list statement allows you to rename the itemList and item variables. item will become <variable> and itemList will become <variable>List.
<dl data-sly-list.child="${currentPage.listChildren}">
<dt>index: ${childList.index}</dt>
<dd>value: ${child.title}</dd>
</dl>
You can also access properties dynamically:
<dl data-sly-list.child="${myObj}">
<dt>key: ${child}</dt>
<dd>value: ${myObj[child]}</dd>
</dl>
resource
data-sly-resource: Includes the result of rendering the indicated resource through the sling resolution and rendering process.
A simple resource include:
<article data-sly-resource="path/to/resource"></article>
Options allow a number of additional variants:
Manipulating the path of the resource:
<article data-sly-resource="${ @ path='path/to/resource'}"></article>
<article data-sly-resource="${'resource' @ prependPath='my/path'}"></article>
<article data-sly-resource="${'my/path' @ appendPath='resource'}"></article>
Add (or replace) a selector:
<article data-sly-resource="${'path/to/resource' @ selectors='selector'}"></article>
Add, replace or remove multiple selectors:
<article data-sly-resource="${'path/to/resource' @ selectors=['s1', 's2']}"></article>
Add a selector to the existing ones:
<article data-sly-resource="${'path/to/resource' @ addSelectors='selector'}"></article>
Remove some selectors from the existing ones:
<article data-sly-resource="${'path/to/resource' @ removeSelectors='selector1'}"></article>
Remove all selectors:
<article data-sly-resource="${'path/to/resource' @ removeSelectors}"></article>
Overrides the resource type of the resource:
<article data-sly-resource="${'path/to/resource' @ resourceType='my/resource/type'}"></article>
Changes the WCM mode:
<article data-sly-resource="${'path/to/resource' @ wcmmode='disabled'}"></article>
By default, the AEM decoration tags are disabled, the decorationTagName option allows to bring them back, and the cssClassName to add classes to that element.
<article data-sly-resource="${'path/to/resource' @ decorationTagName='span',
cssClassName='className'}"></article>
Note:
AEM offers clear and simple logic controling the decoration tags that wrap included elements. For details see Decoration Tag in the developing components documentation.
include
data-sly-include: Replaces the content of the host element with the markup generated by the indicated HTML template file (HTL, JSP, ESP etc.) when it is processed by its corresponding template engine. The rendering context of the included file will not include the current HTL context (that of the including file); Consequently, for inclusion of HTL files, the current data-sly-use would have to be repeated in the included file (In such a case it is usually better to use data-sly-template and data-sly-call)
title: "myTitle",
class: "myClass",
id: "myId"
}
Then,
<div data-sly-attribute="${attrMap}"></div>
produces,
<div title="myTitle" class="myClass" id="myId"></div>
element
data-sly-element: Replaces the element name of the host element.
For example,
<h1 data-sly-element="${titleLevel}">text</h1>
Replaces the h1 with the value of titleLevel.
For security reasons, data-sly-element accepts only the following element names:
a abbr address article aside b bdi bdo blockquote br caption cite code col colgroup
data dd del dfn div dl dt em figcaption figure footer h1 h2 h3 h4 h5 h6 header i ins
kbd li main mark nav ol p pre q rp rt ruby s samp section small span strong sub
sup table tbody td tfoot th thead time tr u var wbr
To set other elements, XSS security must be turned off (@context=’unsafe’).
test
data-sly-test: Conditionally removes the host element and it’s content. A value of false removes the element; a value of true retains the element.
For example, the p element and its content will only be rendered if isShown is true:
<p data-sly-test="${isShown}">text</p>
The result of a test can be assigned to a variable that can be used later. This is usually used to construct “if else” logic, since there is no explicit else statement:
<p data-sly-test.abc="${a || b || c}">is true</p>
<p data-sly-test="${!abc}">or not</p>
The variable, once set, has global scope within the HTL file.
Following are some examples on comparing values:
<div data-sly-test="${properties.jcr:title == 'test'}">TEST</div>
<div data-sly-test="${properties.jcr:title != 'test'}">NOT TEST</div>
<div data-sly-test="${properties['jcr:title'].length > 3}">Title is longer than 3</div>
<div data-sly-test="${properties['jcr:title'].length >= 0}">Title is longer or equal to zero </div>
<div data-sly-test="${properties['jcr:title'].length > aemComponent.MAX_LENGTH}">
Title is longer than the limit of ${aemComponent.MAX_LENGTH}
</div>
repeat
With data-sly-repeat you can repeat an element multiple times based on the list that is specified.
<div data-sly-repeat="${currentPage.listChildren}">${item.name}</div>
This works the same way as data-sly-list, except that you do not need a container element.
The following example shows that you can also refer to the item for attributes:
<div data-sly-repeat="${currentPage.listChildren}" data-sly-attribute.class="${item.name}">${item.name}</div>
list
data-sly-list: Repeats the content of the host element for each enumerable property in the provided object.
Here is a simple loop:
<dl data-sly-list="${currentPage.listChildren}">
<dt>index: ${itemList.index}</dt>
<dd>value: ${item.title}</dd>
</dl>
The following default variables are available within the scope of the list:
item: The current item in the iteration.
itemList: Object holding the following properties:
index: zero-based counter (0..length-1).
count: one-based counter (1..length).
first: true if the current item is the first item.
middle: true if the current item is neither the first nor the last item.
last: true if the current item is the last item.
odd: true if index is odd.
even: true if index is even.
Defining an identifier on the data-sly-list statement allows you to rename the itemList and item variables. item will become <variable> and itemList will become <variable>List.
<dl data-sly-list.child="${currentPage.listChildren}">
<dt>index: ${childList.index}</dt>
<dd>value: ${child.title}</dd>
</dl>
You can also access properties dynamically:
<dl data-sly-list.child="${myObj}">
<dt>key: ${child}</dt>
<dd>value: ${myObj[child]}</dd>
</dl>
resource
data-sly-resource: Includes the result of rendering the indicated resource through the sling resolution and rendering process.
A simple resource include:
<article data-sly-resource="path/to/resource"></article>
Options allow a number of additional variants:
Manipulating the path of the resource:
<article data-sly-resource="${ @ path='path/to/resource'}"></article>
<article data-sly-resource="${'resource' @ prependPath='my/path'}"></article>
<article data-sly-resource="${'my/path' @ appendPath='resource'}"></article>
Add (or replace) a selector:
<article data-sly-resource="${'path/to/resource' @ selectors='selector'}"></article>
Add, replace or remove multiple selectors:
<article data-sly-resource="${'path/to/resource' @ selectors=['s1', 's2']}"></article>
Add a selector to the existing ones:
<article data-sly-resource="${'path/to/resource' @ addSelectors='selector'}"></article>
Remove some selectors from the existing ones:
<article data-sly-resource="${'path/to/resource' @ removeSelectors='selector1'}"></article>
Remove all selectors:
<article data-sly-resource="${'path/to/resource' @ removeSelectors}"></article>
Overrides the resource type of the resource:
<article data-sly-resource="${'path/to/resource' @ resourceType='my/resource/type'}"></article>
Changes the WCM mode:
<article data-sly-resource="${'path/to/resource' @ wcmmode='disabled'}"></article>
By default, the AEM decoration tags are disabled, the decorationTagName option allows to bring them back, and the cssClassName to add classes to that element.
<article data-sly-resource="${'path/to/resource' @ decorationTagName='span',
cssClassName='className'}"></article>
Note:
AEM offers clear and simple logic controling the decoration tags that wrap included elements. For details see Decoration Tag in the developing components documentation.
include
data-sly-include: Replaces the content of the host element with the markup generated by the indicated HTML template file (HTL, JSP, ESP etc.) when it is processed by its corresponding template engine. The rendering context of the included file will not include the current HTL context (that of the including file); Consequently, for inclusion of HTL files, the current data-sly-use would have to be repeated in the included file (In such a case it is usually better to use data-sly-template and data-sly-call)
A simple include:
<section data-sly-include="path/to/template.html"></section>
JSPs can be included the same way:
<section data-sly-include="path/to/template.jsp"></section>
Options let you manipulate the path of the file:
<section data-sly-include="${ @ path='path/to/template.html'}"></section>
<section data-sly-include="${'template.html' @ prependPath='my/path'}"></section>
<section data-sly-include="${'my/path' @ appendPath='template.html'}"></section>
You can also change the WCM mode:
<section data-sly-include="${'template.html' @ wcmmode='disabled'}"></section>
template & call
data-sly-template: Defines a template. The host element and its content are not output by HTL
data-sly-call: Calls a template defined with data-sly-template. The content of the called template (optionally parameterized) replaces the content of the host element of the call.
<section data-sly-include="path/to/template.html"></section>
JSPs can be included the same way:
<section data-sly-include="path/to/template.jsp"></section>
Options let you manipulate the path of the file:
<section data-sly-include="${ @ path='path/to/template.html'}"></section>
<section data-sly-include="${'template.html' @ prependPath='my/path'}"></section>
<section data-sly-include="${'my/path' @ appendPath='template.html'}"></section>
You can also change the WCM mode:
<section data-sly-include="${'template.html' @ wcmmode='disabled'}"></section>
template & call
data-sly-template: Defines a template. The host element and its content are not output by HTL
data-sly-call: Calls a template defined with data-sly-template. The content of the called template (optionally parameterized) replaces the content of the host element of the call.
Define a static template and then call it:
<template data-sly-template.one>blah</template>
<div data-sly-call="${one}"></div>
Define a dynamic template and then call it with parameters:
<template data-sly-template.two="${ @ title}"><h1>${title}</h1></template>
<div data-sly-call="${two @ title=properties.jcr:title}"></div>
Templates located in a different file, can be initialised with data-sly-use. Note that in this case data-sly-use and data-sly-call could also be placed on the same element:
<div data-sly-use.lib="templateLib.html">
<div data-sly-call="${lib.one}"></div>
<div data-sly-call="${lib.two @ title=properties.jcr:title}"></div>
</div>
Template recursion is supported:
<template data-sly-template.nav="${ @ page}">
<ul data-sly-list="${page.listChildren}">
<li>
<div class="title">${item.title}</div>
<div data-sly-call="${nav @ page=item}" data-sly-unwrap></div>
</li>
</ul>
</template>
<div data-sly-call="${nav @ page=currentPage}" data-sly-unwrap></div>
<template data-sly-template.one>blah</template>
<div data-sly-call="${one}"></div>
Define a dynamic template and then call it with parameters:
<template data-sly-template.two="${ @ title}"><h1>${title}</h1></template>
<div data-sly-call="${two @ title=properties.jcr:title}"></div>
Templates located in a different file, can be initialised with data-sly-use. Note that in this case data-sly-use and data-sly-call could also be placed on the same element:
<div data-sly-use.lib="templateLib.html">
<div data-sly-call="${lib.one}"></div>
<div data-sly-call="${lib.two @ title=properties.jcr:title}"></div>
</div>
Template recursion is supported:
<template data-sly-template.nav="${ @ page}">
<ul data-sly-list="${page.listChildren}">
<li>
<div class="title">${item.title}</div>
<div data-sly-call="${nav @ page=item}" data-sly-unwrap></div>
</li>
</ul>
</template>
<div data-sly-call="${nav @ page=currentPage}" data-sly-unwrap></div>
i18n and locale objects
When you are using i18n and HTL, you can now also pass in custom locale objects.
${'Hello World' @ i18n, locale=request.locale}
URL manipulation
A new set of url manipulations is available.
See the following examples on their usage:
Adds the html extension to a path.
<a href="${item.path @ extension = 'html'}">${item.name}</a>
Adds the html extension and a selector to a path.
<a href="${item.path @ extension = 'html', selectors='products'}">${item.name}</a>
Adds the html extension and a fragment (#value) to a path.
<a href="${item.path @ extension = 'html', fragment=item.name}">${item.name}</a>
When you are using i18n and HTL, you can now also pass in custom locale objects.
${'Hello World' @ i18n, locale=request.locale}
URL manipulation
A new set of url manipulations is available.
See the following examples on their usage:
Adds the html extension to a path.
<a href="${item.path @ extension = 'html'}">${item.name}</a>
Adds the html extension and a selector to a path.
<a href="${item.path @ extension = 'html', selectors='products'}">${item.name}</a>
Adds the html extension and a fragment (#value) to a path.
<a href="${item.path @ extension = 'html', fragment=item.name}">${item.name}</a>
HTL Features Supported in AEM 6.3
The following new HTL features are supported in Adobe Experience Manager (AEM) 6.3:
Number/Date-formatting
AEM 6.3 supports native formatting of numbers and dates, without writing custom code. This also supports timezone and locale.
The following examples show that the format is specified first, then the value that needs formatting:
The following new HTL features are supported in Adobe Experience Manager (AEM) 6.3:
Number/Date-formatting
AEM 6.3 supports native formatting of numbers and dates, without writing custom code. This also supports timezone and locale.
The following examples show that the format is specified first, then the value that needs formatting:
<h2>${ 'dd-MMMM-yyyy hh:mm:ss' @
format=currentPage.lastModified,
timezone='PST',
locale='fr'}</h2>
<h2>${ '#.00' @ format=300}</h2>
format=currentPage.lastModified,
timezone='PST',
locale='fr'}</h2>
<h2>${ '#.00' @ format=300}</h2>
data-sly-use with resources
This allows to get resources directly in HTL with data-sly-use and does not require to write code to get the resource.
For example:
<div data-sly-use.product=“/etc/commerce/product/12345”>
${ product.title }
</div>
Request-attributes
In the data-sly-include and data-sly-resource you can now pass requestAttributes in order to use them in the receiving HTL-script.
This allows you to properly pass-in parameters into scripts or components.
<sly data-sly-use.settings="com.adobe.examples.htl.core.hashmap.Settings"
data-sly-include="${ 'productdetails.html' @ requestAttributes=settings.settings}" />
Java-code of the Settings class, the Map is used to pass in the requestAttributes:
public class Settings extends WCMUsePojo {
// used to pass is requestAttributes to data-sly-resource
public Map<String, Object> settings = new HashMap<String, Object>();
@Override
public void activate() throws Exception {
settings.put("layout", "flex");
}
}
For example, via a Sling-Model, you can consume the value of the specified requestAttributes.
In this example, layout is injected via the Map from the Use-class:
@Model(adaptables=SlingHttpServletRequest.class)
public class ProductSettings {
@Inject @Optional @Default(values="empty")
public String layout;
}
Fix for @extension
The @extension works in all scenarios in AEM 6.3, before you could have a result like http://www.adobe.com.html and also checks whether to add or not add the extension.
${ link @ extension = 'html' }
HTL Global Objects
Without having to specify anything, HTL provides access to all objects that were commonly available in JSP after including global.jsp. These objects are in addition to any that may be introduced through the Use-API.
Enumerable Objects
These objects provide convenient access to commonly used information. Their content can be accessed with the dot notation, and they can be iterated-through using data-sly-list or data-sly-repeat.
Variable Name | Description |
---|---|
properties | List of properties of the current Resource. Backed by org.apache.sling.api.resource.ValueMap |
pageProperties | List of page properties of the current Page. Backed by org.apache.sling.api.resource.ValueMap |
inheritedPageProperties | List of inherited page properties of the current Page. Backed by org.apache.sling.api.resource.ValueMap |
Each of the following objects is backed by the corresponding Java object.
The most useful variables in the table below are highlighted in bold.
Variable Name | Description | |
---|---|---|
component | com.day.cq.wcm.api.components.Component | |
componentContext | com.day.cq.wcm.api.components.ComponentContext | |
currentDesign | com.day.cq.wcm.api.designer.Design | |
currentNode | javax.jcr.Node | |
currentPage | com.day.cq.wcm.api.Page | |
currentSession | javax.servlet.http.HttpSession | |
currentStyle | com.day.cq.wcm.api.designer.Style | |
designer | com.day.cq.wcm.api.designer.Designer | |
editContext | com.day.cq.wcm.api.components.EditContext | |
log | org.slf4j.Logger | |
out | java.io.PrintWriter | |
pageManager | com.day.cq.wcm.api.PageManager | |
reader | java.io.BufferedReader | |
request | org.apache.sling.api.SlingHttpServletRequest | |
resolver | org.apache.sling.api.resource.ResourceResolver | |
resource | org.apache.sling.api.resource.Resource | |
resourceDesign | com.day.cq.wcm.api.designer.Design | |
resourcePage | com.day.cq.wcm.api.Page | |
response | org.apache.sling.api.SlingHttpServletResponse | |
sling | org.apache.sling.api.scripting.SlingScriptHelper | |
slyWcmHelper | com.adobe.cq.sightly.WCMScriptHelper | |
wcmmode | com.adobe.cq.sightly.SightlyWCMMode | |
xssAPI | com.adobe.granite.xss.XSSAPI |
There are also objects available that are backed by JavaScript. However, as of AEM 6.2 these objects are still experimental and it is better to use the Java-backed objects, which allow to do the same.
HTL Use-API
The following table gives an overview of the pros and cons of each API.
For page components, it is recommended to use a mixed model, where all model logic is located in Java, providing clear APIs that are agnostic to anything that happens in the view (i.e. within the components). AEM comes with great default models like the Page or the Resource API that should be able to cover most cases.
The following table gives an overview of the pros and cons of each API.
Java Use-API | JavaScript Use-API | |
Pros |
|
|
Cons |
|
|
All view logic that is specific to a component should be placed within that component as JavaScript, because it belongs to that component.
Developer Tools
The following developer tools are available for HTL:
The following developer tools are available for HTL:
- HTL Read Eval Print Loop
- A live execution environment for playing with the language, great for learning.
- AEM Brackets extension
- An easy tool for front-end developers to work on AEM components.
- AEM Eclipse extension
- A tool for back-end developers to create AEM projects and easily edit them.
No comments:
Post a Comment
If you have any doubts or questions, please let us know.