The HTML Template Language (HTL) Java Use-API enables an HTL file to access helper methods in a custom Java class. This allows all complex business logic to be encapsulated in the Java code, while the HTL code deals only with direct markup production.
A Simple Example
We'll start with an HTL component that
does not have a use-class. It consists of a single file,
/apps/my-example/components/info.html
/apps/my-example/component/info/info.html
<div> <h1>${properties.title}</h1> <p>${properties.description}</p> </div>
We also add some content for this component to render at /content/my-example/ :
http://localhost:4502/content/my-example.json
{ "sling:resourceType": "my-example/component/info", "title": "My Example", "description": "This Is Some Example Content." }
When this content is accessed, the HTL file is executed. Within the HTL code, we use the context object
properties to access the current resource's
title and
description and display them. The output HTML will be:
view-source:http://localhost:4502/content/my-example.html
<div> <h1>My Example</h1> <p>This Is Some Example Content.</p> </div>
Adding a Use-Class
The info a component, as it stands, does not need a use-class to perform its (very simple) function. There are cases, however, where you need to do things that cannot be done in HTL and so you need a use-class. But keep in mind the following:
A use-class should only be used when something cannot be done in HTL alone.
For example, suppose that you want the info component to display the title and
description properties of the resource, but all in lowercase. Since HTL does not have a method for lowercasing strings, you will need a use-class. We can do this by adding a Java use-class and changing the info.html as follows:
We'll start with an HTL component that
does not have a use-class. It consists of a single file,
/apps/my-example/components/info.html
/apps/my-example/component/info/info.html
<div> <h1>${properties.title}</h1> <p>${properties.description}</p> </div>
We also add some content for this component to render at /content/my-example/ :
http://localhost:4502/content/my-example.json
{ "sling:resourceType": "my-example/component/info", "title": "My Example", "description": "This Is Some Example Content." }
When this content is accessed, the HTL file is executed. Within the HTL code, we use the context object
properties to access the current resource's
title and
description and display them. The output HTML will be:
view-source:http://localhost:4502/content/my-example.html
<div> <h1>My Example</h1> <p>This Is Some Example Content.</p> </div>
Adding a Use-Class
The info a component, as it stands, does not need a use-class to perform its (very simple) function. There are cases, however, where you need to do things that cannot be done in HTL and so you need a use-class. But keep in mind the following:
A use-class should only be used when something cannot be done in HTL alone.
For example, suppose that you want the info component to display the title and
description properties of the resource, but all in lowercase. Since HTL does not have a method for lowercasing strings, you will need a use-class. We can do this by adding a Java use-class and changing the info.html as follows:
/apps/my-example/component/info/info.html
<div data-sly-use.info="Info"> <h1>${info.lowerCaseTitle}</h1> <p>${info.lowerCaseDescription}</p> </div>
/apps/my-example/component/info/Info.java
package apps.my_example.components.info;
import com.adobe.cq.sightly.WCMUsePojo;
public class Info extends WCMUsePojo {
private String lowerCaseTitle;
private String lowerCaseDescription;
@Override
public void activate() throws Exception {
lowerCaseTitle = getProperties().get("title", "").toLowerCase();
lowerCaseDescription = getProperties().get("description", "").toLowerCase();
}
public String getLowerCaseTitle() {
return lowerCaseTitle;
}
public String getLowerCaseDescription() {
return lowerCaseDescription;
}
}
In the following sections, we walk through the different parts of the code.
Local vs Bundle Java Class
The Java use-class can be installed in two ways: local or bundle .This example uses a local install.
In a local install, the Java source file is placed alongside the HTL file, in the same repository folder. The source is automatically compiled on demand. No separate compilation or packaging step is required.
In a bundle install, the Java class must be compiled and deployed
within an OSGi bundle using the standard AEM bundle deployment mechanism (see Bundled Java Class ).
A local Java use-class is recommended when the use-class is specific to the component in question.
A bundle Java use-class is recommended when the Java code implements a service that is accessed from multiple HTL components.
Java package is repository path
When a local install is used, the the package name of the use-class must match that of the repository folder location, with any hyphens in the path replaced by underscores in the package name.
In this case Info.java is located at /apps/my-example/components/info so the package is apps.my_example.components.info :
<div data-sly-use.info="Info"> <h1>${info.lowerCaseTitle}</h1> <p>${info.lowerCaseDescription}</p> </div>
/apps/my-example/component/info/Info.java
package apps.my_example.components.info;
import com.adobe.cq.sightly.WCMUsePojo;
public class Info extends WCMUsePojo {
private String lowerCaseTitle;
private String lowerCaseDescription;
@Override
public void activate() throws Exception {
lowerCaseTitle = getProperties().get("title", "").toLowerCase();
lowerCaseDescription = getProperties().get("description", "").toLowerCase();
}
public String getLowerCaseTitle() {
return lowerCaseTitle;
}
public String getLowerCaseDescription() {
return lowerCaseDescription;
}
}
In the following sections, we walk through the different parts of the code.
Local vs Bundle Java Class
The Java use-class can be installed in two ways: local or bundle .This example uses a local install.
In a local install, the Java source file is placed alongside the HTL file, in the same repository folder. The source is automatically compiled on demand. No separate compilation or packaging step is required.
In a bundle install, the Java class must be compiled and deployed
within an OSGi bundle using the standard AEM bundle deployment mechanism (see Bundled Java Class ).
A local Java use-class is recommended when the use-class is specific to the component in question.
A bundle Java use-class is recommended when the Java code implements a service that is accessed from multiple HTL components.
Java package is repository path
When a local install is used, the the package name of the use-class must match that of the repository folder location, with any hyphens in the path replaced by underscores in the package name.
In this case Info.java is located at /apps/my-example/components/info so the package is apps.my_example.components.info :
/apps/my-example/component/info/Info.java
package apps.my_example.components.info;
import com.adobe.cq.sightly.WCMUsePojo;
public class Info extends WCMUsePojo {
...
}
Using hyphens in the names of repository items is a recommended practice in AEM development. However, hyphens are illegal within Java package names. For this reason, all hyphens in the repository path must be converted to underscores in the package name.
Extending WCMUsePojo
While there are a number of ways of incorporating a Java class with HTL (see Alternatives to WCMUsePojo ), the simplest is to extend the WCMUsePojo class:
/apps/my-example/component/info/Info.java
package apps.my_example.components.info;
import com.adobe.cq.sightly.WCMUsePojo;
public class Info extends WCMUsePojo {
...
}
package apps.my_example.components.info;
import com.adobe.cq.sightly.WCMUsePojo;
public class Info extends WCMUsePojo {
...
}
Using hyphens in the names of repository items is a recommended practice in AEM development. However, hyphens are illegal within Java package names. For this reason, all hyphens in the repository path must be converted to underscores in the package name.
Extending WCMUsePojo
While there are a number of ways of incorporating a Java class with HTL (see Alternatives to WCMUsePojo ), the simplest is to extend the WCMUsePojo class:
/apps/my-example/component/info/Info.java
package apps.my_example.components.info;
import com.adobe.cq.sightly.WCMUsePojo;
public class Info extends WCMUsePojo {
...
}
Initialising the class
When the use-class is extended from WCMUsePojo, initialisation is performed by overriding the activate method:
/apps/my-example/component/info/Info.java
...
public class Info extends WCMUsePojo {
private String lowerCaseTitle;
private String lowerCaseDescription;
@Override
public void activate() throws Exception {
lowerCaseTitle = getProperties().get("title", "").toLowerCase();
lowerCaseDescription = getProperties().get("description", "").toLowerCase();
}
...
}
Context
Typically, the activate method is used to precompute and store (in member variables) the values needed in your HTL code, based on the current context (the current request and resource, for example).
The WCMUsePojo the class provides access to the same set of context objects as are available within an HTL file (see Global Objects ).
In a class that extends WCMUsePojo , context objects can be accessed
by name using <T> T get(String name, Class<T> type)
Alternatively, commonly used context objects can be accessed directly by the appropriate convenience method :
PageManager getPageManager()
Page getCurrentPage()
Page getResourcePage()
ValueMap getPageProperties()
ValueMap getProperties()
Designer getDesigner()
Design getCurrentDesign()
When the use-class is extended from WCMUsePojo, initialisation is performed by overriding the activate method:
/apps/my-example/component/info/Info.java
...
public class Info extends WCMUsePojo {
private String lowerCaseTitle;
private String lowerCaseDescription;
@Override
public void activate() throws Exception {
lowerCaseTitle = getProperties().get("title", "").toLowerCase();
lowerCaseDescription = getProperties().get("description", "").toLowerCase();
}
...
}
Context
Typically, the activate method is used to precompute and store (in member variables) the values needed in your HTL code, based on the current context (the current request and resource, for example).
The WCMUsePojo the class provides access to the same set of context objects as are available within an HTL file (see Global Objects ).
In a class that extends WCMUsePojo , context objects can be accessed
by name using <T> T get(String name, Class<T> type)
Alternatively, commonly used context objects can be accessed directly by the appropriate convenience method :
PageManager getPageManager()
Page getCurrentPage()
Page getResourcePage()
ValueMap getPageProperties()
ValueMap getProperties()
Designer getDesigner()
Design getCurrentDesign()
Style getCurrentStyle()
Component getComponent()
ValueMap getInheritedProperties()
Resource getResource()
ResourceResolver getResourceResolver()
SlingHttpServletRequest getRequest()
SlingHttpServletResponse. getResponse()
SlingScriptHelper getSlingScriptHelper()
Getter methods
Once the use-class has initialized, the HTL file is run. During this stage HTL will typically pull in the state of various member variables of the use-class and render them for presentation.
To provide access to these values from within the HTL file you must define custom getter methods in the use-class according to the following naming convention :
A method of the form getXyz will expose within the HTL file an object property called xyz .
For example, in the following example, the methods getTitle and getDescription result in the object properties title and description becoming accessible within the context of the HTL file:
/apps/my-example/component/info/Info.java
...
public class Info extends WCMUsePojo {
...
public String getLowerCaseTitle() {
return lowerCaseTitle;
}
public String getLowerCaseDescription() {
return lowerCaseDescription;
}
}
data-sly-use attribute
The data-sly-use attribute is used to initialize the use-class within your HTL code. In our example, the data-sly-use the attribute declares that we want to use the class Info . We can use just the local name of the class because we are using a local install (having placed the Java source file is in the same folder as the HTL file). If we were using a bundle install we would have to specify the fully qualified classname.
Component getComponent()
ValueMap getInheritedProperties()
Resource getResource()
ResourceResolver getResourceResolver()
SlingHttpServletRequest getRequest()
SlingHttpServletResponse. getResponse()
SlingScriptHelper getSlingScriptHelper()
Getter methods
Once the use-class has initialized, the HTL file is run. During this stage HTL will typically pull in the state of various member variables of the use-class and render them for presentation.
To provide access to these values from within the HTL file you must define custom getter methods in the use-class according to the following naming convention :
A method of the form getXyz will expose within the HTL file an object property called xyz .
For example, in the following example, the methods getTitle and getDescription result in the object properties title and description becoming accessible within the context of the HTL file:
/apps/my-example/component/info/Info.java
...
public class Info extends WCMUsePojo {
...
public String getLowerCaseTitle() {
return lowerCaseTitle;
}
public String getLowerCaseDescription() {
return lowerCaseDescription;
}
}
data-sly-use attribute
The data-sly-use attribute is used to initialize the use-class within your HTL code. In our example, the data-sly-use the attribute declares that we want to use the class Info . We can use just the local name of the class because we are using a local install (having placed the Java source file is in the same folder as the HTL file). If we were using a bundle install we would have to specify the fully qualified classname.
/apps/my-example/component/info/info.html
<div data-sly-use.info="Info"> <h1>${info.lowerCaseTitle}</h1> <p>${info.lowerCaseDescription}</p> </div>
<div data-sly-use.info="Info"> <h1>${info.lowerCaseTitle}</h1> <p>${info.lowerCaseDescription}</p> </div>
Local identifier The identifier ' info' (after the dot in data-sly-use.info ) is used within the HTL file to identify the class. The scope of this identifier is global within the file after it has been declared. It is not limited to the element that contains the
data-sly-use statement.
/apps/my-example/component/info/info.html
<div data-sly-use.info="Info"> <h1>${info.lowerCaseTitle}</h1> <p>${info.lowerCaseDescription}</p> </div>
Getting properties
The identifier info is then used to access the object properties title and description that was exposed through the getter methods Info.getTitle and Info.getDescription .
/apps/my-example/component/info/info.html
<div data-sly-use.info="Info"> <h1>${info.lowerCaseTitle}</h1> <p>${info.lowerCaseDescription}</p> </div>
Output
Now, when we access /content/my-example.html it will return the following HTML:
view-source: http://localhost:4502/content/my-example.html
<div> <h1>my example</h1> <p>this is some example content.</p> </div>
Beyond The Basics
In this section we'll introduce some further features that go beyond the simple example above: Passing parameters to a use-class. Bundled Java use-class. Alternatives to WCMUsePojo Passing Parameters Parameters can be passed to a use-class upon initialization. For example, we could do something like this:
/content/my-example/component/info/info.html
<div data-sly-use.info="${'Info' @ text='Some text'}"> <h1>${info.lowerCaseTitle}</h1> <p>${info.lowerCaseDescription}</p> <p>${info.upperCaseText}</p> </div>
Here we are passing a parameter called text . The use-class then uppercases the string we retrieve and display the result with info.upperCaseText . Here is the adjusted use-class:
data-sly-use statement.
/apps/my-example/component/info/info.html
<div data-sly-use.info="Info"> <h1>${info.lowerCaseTitle}</h1> <p>${info.lowerCaseDescription}</p> </div>
Getting properties
The identifier info is then used to access the object properties title and description that was exposed through the getter methods Info.getTitle and Info.getDescription .
/apps/my-example/component/info/info.html
<div data-sly-use.info="Info"> <h1>${info.lowerCaseTitle}</h1> <p>${info.lowerCaseDescription}</p> </div>
Output
Now, when we access /content/my-example.html it will return the following HTML:
view-source: http://localhost:4502/content/my-example.html
<div> <h1>my example</h1> <p>this is some example content.</p> </div>
Beyond The Basics
In this section we'll introduce some further features that go beyond the simple example above: Passing parameters to a use-class. Bundled Java use-class. Alternatives to WCMUsePojo Passing Parameters Parameters can be passed to a use-class upon initialization. For example, we could do something like this:
/content/my-example/component/info/info.html
<div data-sly-use.info="${'Info' @ text='Some text'}"> <h1>${info.lowerCaseTitle}</h1> <p>${info.lowerCaseDescription}</p> <p>${info.upperCaseText}</p> </div>
Here we are passing a parameter called text . The use-class then uppercases the string we retrieve and display the result with info.upperCaseText . Here is the adjusted use-class:
/apps/my-example/component/info/Info.java
package
apps.my_example.components.info
; import com.adobe.cq.sightly.WCMUsePojo; public class Info extends WCMUsePojo { ... private String reverseText; @Override public void activate() throws Exception { ... String text = get("text", String.class); reverseText = new StringBuffer(text).reverse().toString(); } public String getReverseText() { return reverseText; } ... }
The parameter is accessed through the WCMUsePojo method
<T> T get(String paramName, Class<T> type)
In our case, the statement get("text", String.class) The string is then reversed and exposed via the method getReverseText() Only Pass Parameters from data-sly-template While the above example is technically correct, it actually does not make much sense to pass a value from HTL to initialize a use-class, when the value in question is available in the execution context of the HTL code (or, trivially, the value is static, as above).
The reason is that the use-class will always have access to the same execution context as the HTL code. This brings up an import point of best practice:
Passing a parameter to a use-class should only be done when the use-class is used in a data-sly-template file which itself is called from another HTL file with parameters that need to be passed on.
For example, let's create a separate data-sly-template file alongside our existing example. We will call the new file extra.html . It contains a
data-sly-templateblock called extra :
/apps/my-example/component/info/extra.html
<template data-sly-template.extra="${@ text}" data-sly-use.extraHelper="${'ExtraHelper' @ text=text}"> <p>${extraHelper.reversedText}</p> </template>
The template extra , takes a single parameter, text . It then initializes the Java use-class ExtraHelper with the local name extraHelper and passes it the value of the template parameter text as the use-class parameter text .
The body of the template gets the property
extraHelper.reversedText (which, under the hood, actually calls
ExtraHelper.getReversedText() ) and displays that value.
We also adapt our existing
info.html to use this new template:
/apps/my-example/component/info/info.html
<div data-sly-use.info="Info" data-sly-use.extra="extra.html"> <h1>${info.lowerCaseTitle}</h1> <p>${info.lowerCaseDescription}</p> <div data-sly-call="${extra.extra @ text=properties.description}"></div> </div>
The file info.html now contains two data-sly-use statements, the original one that imports the Info Java use-class and a new one that imports the template file under the local name extra. Note that we could have placed the template block inside the
info.html file to avoid the second data-sly-use , but a separate template file is more common, and more reusable.
The Info class is employed as before, calling its getter methods
getLowerCaseTitle() and getLowerCaseDescription() through their corresponding HTL properties info.lowerCaseTitle and info.lowerCaseDescription .
Then we perform a data-sly-call to the template extra and pass it the value properties.description as the parameter text .
The Java use-class Info.java is changed to handle the new text parameter:
/apps/my-example/component/info/ExtraHelper.java
package apps.my_example.components.info; import com.adobe.cq.sightly.WCMUsePojo; public class ExtraHelper extends WCMUsePojo { private String reversedText; ... @Override public void activate() throws Exception { String text = get("text", String.class); reversedText = new StringBuilder(text).reverse().toString(); ... } public String getReversedText() { return reversedText; } }
The text parameter is retrieved with get("text", String.class) , the value is reversed and made available as the HTL object reversedText through the getter getReversedText() .
Bundled Java Class
With a bundle use-class, the class must be compiled, packaged, and deployed in AEM using the standard OSGi bundle deployment mechanism. In contrast with a local install, the use-class
package declaration should be named normally:
/apps/my-example/component/info/Info.java
package org.example.app.components; import com.adobe.cq.sightly.WCMUsePojo; public class Info extends WCMUsePojo { ... } and, the data-sly-use the the the statement must reference the fully qualified class name , as opposed to just the local class name:
/apps/my-example/component/info/info.html
<div data-sly-use.info="org.example.app.components.info.Info"> <h1>${info.title}</h1> <p>${info.description}</p> </div>
Alternatives to WCMUsePojo
The most common way to create a Java use-class is to extend
WCMUsePojo. However, there are a number of other options. To understand these variants it helps to understand how the HTL data-sly-use the statement works under the hood.
Suppose you have the following
data-sly-use statement:
<div data-sly-use. localName="UseClass">
The system processes the statement as follows:
(1) If there exists a local file UseClass.java in the same directory as the HTL file, try to compile and load that class. If successful go to (2).
Otherwise, interpret UseClass as a fully qualified class name
and try to load it from the OSGi environment. If successful go to (2). Otherwise, interpret UseClass as a path to an HTL or JavaScript file and load that file. If successful goto (4).
(2)Try to adapt the current Resource to UseClass. If successful, go to (3).
Otherwise, try to adapt the current Request to UseClass . If successful, go to (3).
Otherwise, try to instantiate UseClass with a zero-argument constructor. If successful, go to (3).
(3) Within HTL, bind the newly adapted or created object to the name localName. If UseClass implements io.sightly.java.api.Use then call the init method, passing the current execution context (in the form of a javax.scripting.Bindings object).
(4) If UseClass is a path to an HTL file containing a data-sly-template , prepare the template. Otherwise, if UseClass is a path to a JavaScript use-class, prepare the use-class (see JavaScript Use-API ).
A few significant points about the above description:
Any class that is adaptable from Resource , adaptable from Request , or that has a zero-argument constructor can be a use-class. The class does not have to extend extend WCMUsePojo or even implement Use .
However, if the use-class does implement Use , then its
init method will automatically be called with the current context, allowing you to place initialization code there that depends on that context.
A use-class that extends WCMUsePojo is just a special case of implementing Use . It provides the convenience context methods and its activate method is automatically called fromUse.init.
Directly Implement Interface Use
While the most common way to create a use-class is to extend
WCMUsePojo , it is also possible to directly implement the
[
io.sightly.java.api.Use
](https://helpx.adobe.com/experience-manager/6-2/sites/developing/using/reference-materials/javadoc/io/sightly/java/api/Use.html) interface itself.
The Use the interface defines only one method:
[public void init(javax.script.Bindings bindings)](https://helpx.adobe.com/experience-manager/6-2/sites/developing/using/reference-materials/javadoc/io/sightly/java/api/Use#init(javax.script.Bindings))
The init method will be called on initialization of the class with a
Bindings an object that holds all the context objects and any parameters passed into the use-class.
All additional functionality (such as the equivalent of
WCMUsePojo.getProperties() ) must be implemented explicitly using the
[javax.script.Bindings](http://docs.oracle.com/javase/7/docs/api/javax/script/Bindings.html) object. For example:
Info.java
import io.sightly.java.api.Use; public class MyComponent implements Use { ... @Override public void init(Bindings bindings) { // All standard objects/binding are available Resource resource = (Resource)bindings.get("resource"); ValueMap properties = (ValueMap)bindings.get("properties"); ... // Parameters passed to the use-class are also available String param1 = (String) bindings.get("param1"); } ... }
The main case for implementing the
Use interface yourself instead of extending
WCMUsePojo is when you wish to use a subclass of an already existing class as the use-class.
Adaptable from Resource
Another option is to use a helper class that is adaptable from org.apache.sling.api.resource.Resource .
Let's say you need to write an HTL script that displays the mimetype of a DAM asset. In this case, you know that when your HTL script is called, it will be within the context of a Resource that wraps a JCR Node with nodetype dam:Asset .
You know that a dam:Asset node has a structure like this:
Repository Structure
{ "content": { "dam": { "geometrixx": { "portraits": { "jane_doe.jpg": { ... "jcr:content": { ... "metadata": { ... }, "renditions": { ... "original": { ... "jcr:content": { "jcr:primaryType": "nt:resource", "jcr:lastModifiedBy": "admin", "jcr:mimeType": "image/jpeg", "jcr:lastModified": "Fri Jun 13 2014 15:27:39 GMT+0200", "jcr:data": ..., "jcr:uuid": "22e3c598-4fa8-4c5d-8b47-8aecfb5de399" } }, "cq5dam.thumbnail.319.319.png": { ... }, "cq5dam.thumbnail.48.48.png": { ... }, "cq5dam.thumbnail.140.100.png": { ... } } } } } } } }
Here we show the asset (a JPEG image) that comes with a default install of AEM as part of the example project geometrixx. The asset is called
jane_doe.jpg and its mimetype is image/jpeg .
To access the asset from within HTL, you can declare
[com.day.cq.dam.api.Asset](https://helpx.adobe.com/experience-manager/6-2/sites/developing/using/reference-materials/javadoc/com/adobe/granite/asset/api/Asset.html) as the class in the
data-sly-use statement: and then use a get method of
Asset to retrieve the desired information. For example:
mimetype.html
<div data-sly-use.asset="com.day.cq.dam.api.Asset"> <p>${asset.mimeType}</p> </div>
The data-sly-use statement directs HTL to adapt the current Resource to an
Asset and give it the local name asset . It then calls the getMimeType method of Asset using the HTL getter shorthand: asset.mimeType .
Adaptable from Request
It is also possible to emply as a use-class any class that is adaptable from
[org.apache.sling.api.SlingHttpServletRequest](https://helpx.adobe.com/experience-manager/6-2/sites/developing/using/reference-materials/javadoc/org/apache/sling/api/SlingHttpServletRequest.html)
As with the above case of a use-class adaptable from
Resource , a use-class adaptable from
SlingHttpServletRequest can be specified in the
data-sly-use statement. Upon execution, the current request will be adapted to the class given and the resulting object will be made available within HTL.
package
apps.my_example.components.info
; import com.adobe.cq.sightly.WCMUsePojo; public class Info extends WCMUsePojo { ... private String reverseText; @Override public void activate() throws Exception { ... String text = get("text", String.class); reverseText = new StringBuffer(text).reverse().toString(); } public String getReverseText() { return reverseText; } ... }
The parameter is accessed through the WCMUsePojo method
<T> T get(String paramName, Class<T> type)
In our case, the statement get("text", String.class) The string is then reversed and exposed via the method getReverseText() Only Pass Parameters from data-sly-template While the above example is technically correct, it actually does not make much sense to pass a value from HTL to initialize a use-class, when the value in question is available in the execution context of the HTL code (or, trivially, the value is static, as above).
The reason is that the use-class will always have access to the same execution context as the HTL code. This brings up an import point of best practice:
Passing a parameter to a use-class should only be done when the use-class is used in a data-sly-template file which itself is called from another HTL file with parameters that need to be passed on.
For example, let's create a separate data-sly-template file alongside our existing example. We will call the new file extra.html . It contains a
data-sly-templateblock called extra :
/apps/my-example/component/info/extra.html
<template data-sly-template.extra="${@ text}" data-sly-use.extraHelper="${'ExtraHelper' @ text=text}"> <p>${extraHelper.reversedText}</p> </template>
The template extra , takes a single parameter, text . It then initializes the Java use-class ExtraHelper with the local name extraHelper and passes it the value of the template parameter text as the use-class parameter text .
The body of the template gets the property
extraHelper.reversedText (which, under the hood, actually calls
ExtraHelper.getReversedText() ) and displays that value.
We also adapt our existing
info.html to use this new template:
/apps/my-example/component/info/info.html
<div data-sly-use.info="Info" data-sly-use.extra="extra.html"> <h1>${info.lowerCaseTitle}</h1> <p>${info.lowerCaseDescription}</p> <div data-sly-call="${extra.extra @ text=properties.description}"></div> </div>
The file info.html now contains two data-sly-use statements, the original one that imports the Info Java use-class and a new one that imports the template file under the local name extra. Note that we could have placed the template block inside the
info.html file to avoid the second data-sly-use , but a separate template file is more common, and more reusable.
The Info class is employed as before, calling its getter methods
getLowerCaseTitle() and getLowerCaseDescription() through their corresponding HTL properties info.lowerCaseTitle and info.lowerCaseDescription .
Then we perform a data-sly-call to the template extra and pass it the value properties.description as the parameter text .
The Java use-class Info.java is changed to handle the new text parameter:
/apps/my-example/component/info/ExtraHelper.java
package apps.my_example.components.info; import com.adobe.cq.sightly.WCMUsePojo; public class ExtraHelper extends WCMUsePojo { private String reversedText; ... @Override public void activate() throws Exception { String text = get("text", String.class); reversedText = new StringBuilder(text).reverse().toString(); ... } public String getReversedText() { return reversedText; } }
The text parameter is retrieved with get("text", String.class) , the value is reversed and made available as the HTL object reversedText through the getter getReversedText() .
Bundled Java Class
With a bundle use-class, the class must be compiled, packaged, and deployed in AEM using the standard OSGi bundle deployment mechanism. In contrast with a local install, the use-class
package declaration should be named normally:
/apps/my-example/component/info/Info.java
package org.example.app.components; import com.adobe.cq.sightly.WCMUsePojo; public class Info extends WCMUsePojo { ... } and, the data-sly-use the the the statement must reference the fully qualified class name , as opposed to just the local class name:
/apps/my-example/component/info/info.html
<div data-sly-use.info="org.example.app.components.info.Info"> <h1>${info.title}</h1> <p>${info.description}</p> </div>
Alternatives to WCMUsePojo
The most common way to create a Java use-class is to extend
WCMUsePojo. However, there are a number of other options. To understand these variants it helps to understand how the HTL data-sly-use the statement works under the hood.
Suppose you have the following
data-sly-use statement:
<div data-sly-use. localName="UseClass">
The system processes the statement as follows:
(1) If there exists a local file UseClass.java in the same directory as the HTL file, try to compile and load that class. If successful go to (2).
Otherwise, interpret UseClass as a fully qualified class name
and try to load it from the OSGi environment. If successful go to (2). Otherwise, interpret UseClass as a path to an HTL or JavaScript file and load that file. If successful goto (4).
(2)Try to adapt the current Resource to UseClass. If successful, go to (3).
Otherwise, try to adapt the current Request to UseClass . If successful, go to (3).
Otherwise, try to instantiate UseClass with a zero-argument constructor. If successful, go to (3).
(3) Within HTL, bind the newly adapted or created object to the name localName. If UseClass implements io.sightly.java.api.Use then call the init method, passing the current execution context (in the form of a javax.scripting.Bindings object).
(4) If UseClass is a path to an HTL file containing a data-sly-template , prepare the template. Otherwise, if UseClass is a path to a JavaScript use-class, prepare the use-class (see JavaScript Use-API ).
A few significant points about the above description:
Any class that is adaptable from Resource , adaptable from Request , or that has a zero-argument constructor can be a use-class. The class does not have to extend extend WCMUsePojo or even implement Use .
However, if the use-class does implement Use , then its
init method will automatically be called with the current context, allowing you to place initialization code there that depends on that context.
A use-class that extends WCMUsePojo is just a special case of implementing Use . It provides the convenience context methods and its activate method is automatically called fromUse.init.
Directly Implement Interface Use
While the most common way to create a use-class is to extend
WCMUsePojo , it is also possible to directly implement the
[
io.sightly.java.api.Use
](https://helpx.adobe.com/experience-manager/6-2/sites/developing/using/reference-materials/javadoc/io/sightly/java/api/Use.html) interface itself.
The Use the interface defines only one method:
[public void init(javax.script.Bindings bindings)](https://helpx.adobe.com/experience-manager/6-2/sites/developing/using/reference-materials/javadoc/io/sightly/java/api/Use#init(javax.script.Bindings))
The init method will be called on initialization of the class with a
Bindings an object that holds all the context objects and any parameters passed into the use-class.
All additional functionality (such as the equivalent of
WCMUsePojo.getProperties() ) must be implemented explicitly using the
[javax.script.Bindings](http://docs.oracle.com/javase/7/docs/api/javax/script/Bindings.html) object. For example:
Info.java
import io.sightly.java.api.Use; public class MyComponent implements Use { ... @Override public void init(Bindings bindings) { // All standard objects/binding are available Resource resource = (Resource)bindings.get("resource"); ValueMap properties = (ValueMap)bindings.get("properties"); ... // Parameters passed to the use-class are also available String param1 = (String) bindings.get("param1"); } ... }
The main case for implementing the
Use interface yourself instead of extending
WCMUsePojo is when you wish to use a subclass of an already existing class as the use-class.
Adaptable from Resource
Another option is to use a helper class that is adaptable from org.apache.sling.api.resource.Resource .
Let's say you need to write an HTL script that displays the mimetype of a DAM asset. In this case, you know that when your HTL script is called, it will be within the context of a Resource that wraps a JCR Node with nodetype dam:Asset .
You know that a dam:Asset node has a structure like this:
Repository Structure
{ "content": { "dam": { "geometrixx": { "portraits": { "jane_doe.jpg": { ... "jcr:content": { ... "metadata": { ... }, "renditions": { ... "original": { ... "jcr:content": { "jcr:primaryType": "nt:resource", "jcr:lastModifiedBy": "admin", "jcr:mimeType": "image/jpeg", "jcr:lastModified": "Fri Jun 13 2014 15:27:39 GMT+0200", "jcr:data": ..., "jcr:uuid": "22e3c598-4fa8-4c5d-8b47-8aecfb5de399" } }, "cq5dam.thumbnail.319.319.png": { ... }, "cq5dam.thumbnail.48.48.png": { ... }, "cq5dam.thumbnail.140.100.png": { ... } } } } } } } }
Here we show the asset (a JPEG image) that comes with a default install of AEM as part of the example project geometrixx. The asset is called
jane_doe.jpg and its mimetype is image/jpeg .
To access the asset from within HTL, you can declare
[com.day.cq.dam.api.Asset](https://helpx.adobe.com/experience-manager/6-2/sites/developing/using/reference-materials/javadoc/com/adobe/granite/asset/api/Asset.html) as the class in the
data-sly-use statement: and then use a get method of
Asset to retrieve the desired information. For example:
mimetype.html
<div data-sly-use.asset="com.day.cq.dam.api.Asset"> <p>${asset.mimeType}</p> </div>
The data-sly-use statement directs HTL to adapt the current Resource to an
Asset and give it the local name asset . It then calls the getMimeType method of Asset using the HTL getter shorthand: asset.mimeType .
Adaptable from Request
It is also possible to emply as a use-class any class that is adaptable from
[org.apache.sling.api.SlingHttpServletRequest](https://helpx.adobe.com/experience-manager/6-2/sites/developing/using/reference-materials/javadoc/org/apache/sling/api/SlingHttpServletRequest.html)
As with the above case of a use-class adaptable from
Resource , a use-class adaptable from
SlingHttpServletRequest can be specified in the
data-sly-use statement. Upon execution, the current request will be adapted to the class given and the resulting object will be made available within HTL.
No comments:
Post a Comment
If you have any doubts or questions, please let us know.