Sightly was introduced by AEM 6.0 to replace JSP by a new HTML Templating System. This post is a quick reference about Sightly. A list of best practices and things to know.
Java class or server side Javascript
With Sightly, you can create a component with two methods. By using Java Class or Javascript Server-Side File. Personnaly, I always use Java Class, but Javascript seems to be a really good deal if you want to develop your components with one language. But keep in mind that AEM Java API offer more functionnalities than Javascript API.
- Sigthly component with Java
packageapps.mycomponent;
import com.adobe.cq.sightly.WCMUse;
publicclassMyComponentextendsWCMUse{
privateStringtitle;
privateStringdescription;
@Override
publicvoidactivate()throwsException{
title=getProperties().get("title","");
description=getProperties().get("description","");
}
// Must have to get back the value in html file
// Explanation : 'get' + capitalize method name
publicStringgetTitle(){
returntitle;
}
publicStringgetDescription(){
returndescription;
}
}
- Sigthly HTML code
<div id="my-component"data-sly-use.cpt="apps.mycomponent.MyComponent">
<!--/* Here you call the Java Method */-->
<!--/* Explanation : Imported Class + '.' + Uncapitalized java method name withoud 'get' */-->
<h1>${cpt.title}</h1>
<p>${cpt.description}</p>
</div>
- Sightly Component with server side Javascript
use(function(){
vartitle=currentPage.getTitle();
vardescription=properties.get("jcr:description","default desc");
return{
title:title,
description:description
};
});
You do have to know that you cannot pass argument to the method. But it is possible to pass argument at the activation of the Java/Javascript class. Example with Java Class :
packageapps.mycomponent;
import com.adobe.cq.sightly.WCMUse;
publicclassMyComponentextendsWCMUse{
privateStringtitle;
privateStringdescription;
@Override
publicvoidactivate()throwsException{
title=get("title",String.class);
}
publicStringgetTitle(){
returntitle;
}
}
Example HTML code
<div id="my-component"data-sly-use.cpt="apps.mycomponent.MyComponent @ title='my title'">
<h1>${cpt.title}</h1>
<p>${cpt.description}</p>
</div>
Context Objects
With Sightly you can directly use component/page properties in HTML file like JSP does. You can access to a wide range of Java Objects (currentPage, currentNode….) too :
Variable Name | Description |
---|---|
properties | List of properties of the current Resource. |
pageProperties | List of page properties of the current Page. |
inheritedPageProperties | List of inherited page properties of the current Page. |
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 |
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.day.cq.wcm.api.WCMMode (in Sightly, evaluates to a string, either“EDIT” or“DESIGN”) |
xssAPI | com.adobe.granite.xss.impl.XSSAPImpl |
All these objects are accessible using Sightly, server side Javascript and Java class.
Examples:<!--/* Access to simple component property defined in dialog.xml */-->
${properties.my_property}
<!--/* Access to JCR property */-->
${properties.jcr:title}
<!--/* Access to page property */-->
${pageProperties.name}
<!--/* Access to inherited page property */-->
${inheritedPageProperties.inherited_property}
<!--/* Visualize all accessible properties */-->
${properties}
If you want to modify the value before accessing a property you can obviously use a Java Class/Server side Javascript as shown in the first part.
Conditional operators
With sightly you can use conditional operators :
${properties.jcr:title||properties.my_title}
${properties.my_title?properties.my_title:properties.jcr:title}
You should mix conditional operators and data-sly-test block statement.
<!--/* If/Else exemples */-->
<pdata-sly-test="${properties.my_property}">Ok</p>
<pdata-sly-test="${!properties.my_property}">Ko</p>
<pdata-sly-test="${properties.article_number < 5}">Number<5</p>
<pdata-sly-test="${properties.article_number == 5}">Number=5</p>
<pdata-sly-test="${properties.article_number > 5}">Number>5</p>
String Formatting
<span>${'Hello {0} {1}'@format=[properties.civility,properties.name]}</span>
I18n / Internationalization
<!--/* Translates the 'Welcome' string to the language of the current page */-->
<p>${'Welcome'@i18n}</p>
<!--/* Translates the 'Welcome' string to a specify language (french in our case) */-->
<p>${'Welcome'@i18n,locale='fr'}</p>
<!--/* You can mix string formatting and internationalization */-->
<p>${'Welcome {0} {1}'@i18n,locale='fr',format=[civility,name]}</p>
Display Context
If you want to write a resource property as an HTML attribute or as an JS comment, the display context is different. That’s why sightly provides some context which will format/filter the property value.
HTML context:
Context Name | When to use | What it does |
---|---|---|
text | Default for content inside elements | Encodes all HTML special characters. |
html | To safely output markup | Filters HTML to meet the AntiSamy policy rules, removing what doesn’t match the rules. |
attribute | Default for attribute values | Encodes all HTML special characters. |
uri | To display links and paths Default for href and src attribute values | Validates URI for writing as an href orsrc attribute value, outputs nothing if validation fails. |
number | To display numbers | Validates URI for containing an integer, outputs zero if validation fails. |
attributeName | Default for data-sly-attribute when setting attribute names | Validates the attribute name, outputs nothing if validation fails. |
elementName | Default for data-sly-element | Validates the element name, outputs nothing if validation fails. |
Javascript context:
Context | When to use | What it does |
---|---|---|
scriptToken | For JS identifiers, literal numbers, or literal strings | Validates the JavaScript token, outputs nothing if validation fails. |
scriptString | Within JS strings | Encodes characters that would break out of the string. |
scriptComment | Within JS comments | Validates the JavaScript comment, outputs nothing if validation fails. |
CSS context:
Context | When to use | What it does |
---|---|---|
styleToken | For CSS identifiers, numbers, dimensions, strings, hex colours or functions. | Validates the CSS token, outputs nothing if validation fails. |
styleString | Within CSS strings | Encodes characters that would break out of the string. |
styleComment | Within CSS comments | Validates the CSS comment, outputs nothing if validation fails. |
Other context :
Context | When to use | What it does |
---|---|---|
unsafe | Only if none of the above does the job | Disables escaping and XSS protection completely. |
Display context can be used as below
<!--/* write html text in a div */-->
<div>${properties.wysiwygText@context='html'}</div>
<!--/* write style token */-->
<span style="color: ${properties.color @ context='styleToken'};">Welcome</span>
<!--/* possible but BAD IDEA (I think...) ! */-->
<div>${script@context='unsafe'}</div>
Block statement
data-sly-use : Initializes a helper object (defined in JavaScript or Java) and exposes it through a variable
<!--/* java class */-->
<div id="my-component"data-sly-use.cpt="apps.mycomponent.MyComponent">
<h1>${cpt.title}</h1>
<p>${cpt.description}</p>
</div>
<!--/* js class */-->
<div id="my-component"data-sly-use.cpt="cpt.js">
<h1>${cpt.title}</h1>
<p>${cpt.description}</p>
</div>
data-sly-unwrap : Removes the host HTML element.
<!--/* This */-->
<pdata-sly-unwrap>Hello World</p>
<!--/* Produces */-->
Hello World
data-sly-attribute : Adds HTML attributes to the host element.
<!--/* This */-->
<div data-sly-attribute.class="${properties.myClass}"></div>
<!--/* Produces */-->
<div class="colorRed"></div>
With a map of attributes
attributes={
title:"beautiful title",
class:"testClass",
}
<!--/* This */-->
<div data-sly-attribute="${attributes}"></div>
<!--/* Produces */-->
<div title="beautiful title"class="testClass"></div>
data-sly-test : Conditionally removes the HTML host element.
<pdata-sly-test="${properties.my_property}">Ok</p>
<pdata-sly-test="${!properties.my_property}">Ko</p>
data-sly-list : Repeats the content of the host element for each enumerable property in the provided object.
<ul data-sly-list="${properties}">
<li>index:${itemList.index}</li>
<li>value:${item}</li>
</ul>
ItemList is an object that is available in the data-sly-list statement and provide the 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
data-sly-resource : Includes the indicated resource.
<!--/* Include the component image */!-->
<div data-sly-resource="${'image' @ resourceType='wcm/foundation/components/image'}"></div>
data-sly-template / data-sly-call : Defines a template and call it.
<!--/* Define the template */-->
<template data-sly-template.titleTemplate="${ @ title}">
<h1>${title}
</template>
<!--/* Call the template */-->
<div data-sly-call="${titleTemplate@ title='title test'}"data-sly-unwrap></div>
You can define your template in a different file :
<!--/* Define the template in template.html */-->
<template data-sly-template.title="${ @ title}">
<h1>${title}
</template>
<!--/*Call it inindex.html/*-->
<div data-sly-use.template="template.html">
<div data-sly-call="${template.title @ title='testTitle'}"></div>
</div>
Clientlibs:
<!--/* Do not forget the data-sly-use ! */-->
<div data-sly-use.clientLib="${'/libs/granite/sightly/templates/clientlib.html'}">
<meta data-sly-call="${clientLib.css @ categories='myClientlib1'}"data-sly-unwrap/>></meta>
<meta data-sly-call="${clientLib.js @ categories='myClientlib2'}"data-sly-unwrap/>></meta>
<meta data-sly-call="${clientLib.all @ categories=['myClientlib1', 'myClientlib2']}"data-sly-unwrap/>></meta>
</div