HTL Tutorial #8: data-sly-use - Loading Java and JavaScript Logic
HTL Tutorial #8: data-sly-use - Loading Java and JavaScript Logic
What is data-sly-use?
data-sly-use loads external logic (Java or JavaScript) and makes it available as a variable in the HTL template.
Key principle: Complex logic belongs in Java/JS, not in the HTL template!
Basic Syntax
<div data-sly-use.varName="path.to.java.Class">
${varName.property}
</div>Loading Java Sling Models
1. Create the Sling Model
package com.example.models;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
@Model(adaptables = Resource.class)
public class AuthorModel {
@ValueMapValue
private String name;
@ValueMapValue
private String bio;
@ValueMapValue
private String avatar;
// Getters
public String getName() {
return name;
}
public String getBio() {
return bio;
}
public String getAvatar() {
return avatar != null ? avatar : "/images/default-avatar.png";
}
// Additional logic
public String getShortBio() {
if (bio != null && bio.length() > 100) {
return bio.substring(0, 100) + "...";
}
return bio;
}
public boolean hasBio() {
return bio != null && !bio.isEmpty();
}
}2. Use in HTL Template
<div data-sly-use.author="com.example.models.AuthorModel">
<!-- Access model methods -->
<h2>${author.name}</h2>
<img src="${author.avatar}" alt="${author.name}">
<!-- Method with logic -->
<p data-sly-test="${author.hasBio}">
${author.shortBio}
</p>
</div>Output:
<div>
<h2>John Smith</h2>
<img src="/images/john.jpg" alt="John Smith">
<p>AEM Developer with 10 years of experience...</p>
</div>Loading JavaScript Scripts
1. Create the JS Script
File: /apps/mysite/components/author/author.js
"use strict";
use(function () {
var name = properties.get("name", "");
var bio = properties.get("bio", "");
return {
name: name,
bio: bio,
shortBio: bio.length > 100 ? bio.substring(0, 100) + "..." : bio,
hasBio: bio && bio.length > 0,
avatar: properties.get("avatar", "/images/default-avatar.png")
};
});2. Use in Template
<!-- Load local JS -->
<div data-sly-use.author="author.js">
<h2>${author.name}</h2>
<p>${author.shortBio}</p>
</div>Passing Parameters
You can pass parameters to the model/script using the Options object:
Syntax
<div data-sly-use.model="${'com.example.Model' @ param1='value', param2=123}">
${model.result}
</div>Java Example
Model:
@Model(adaptables = Resource.class)
public class ProductModel {
@Inject
private Resource resource;
private String category;
private int maxResults;
@PostConstruct
protected void init() {
// Get parameters from Options
ValueMap props = resource.getValueMap();
category = props.get("category", String.class);
maxResults = props.get("maxResults", 10);
}
public List<Product> getProducts() {
// Use parameters to filter
return productService.findByCategory(category, maxResults);
}
}HTL:
<div data-sly-use.products="${'com.example.ProductModel' @ category='electronics', maxResults=5}">
<ul data-sly-list.product="${products.products}">
<li>${product.name}</li>
</ul>
</div>JavaScript Example
Script products.js:
use(function () {
var category = this.category || "all";
var maxResults = this.maxResults || 10;
// Use parameters
var products = getProductsByCategory(category, maxResults);
return {
products: products,
count: products.length
};
});HTL:
<div data-sly-use.data="${'products.js' @ category='books', maxResults=3}">
<p>${data.count} products found</p>
</div>Variable Scope
Variables created with data-sly-use are available:
- In the element and all its children
- Not outside the element
<div data-sly-use.model="com.example.Model">
${model.title} <!-- OK -->
<p>${model.description}</p> <!-- OK -->
</div>
${model.title} <!-- ERROR - out of scope! -->Global Use (Root Element)
To make it available throughout the template:
<div data-sly-use.page="com.example.PageModel">
<!-- All content can use 'page' -->
<header>
<h1>${page.title}</h1>
</header>
<main>
<p>${page.content}</p>
</main>
<footer>
<p>${page.author}</p>
</footer>
</div>Common Patterns
1. Component Model
Typical AEM component structure:
/apps/mysite/components/article/
├── article.html (HTL template)
├── ArticleModel.java (Sling Model)
├── _cq_dialog.xml (dialog)
└── .content.xml (metadata)article.html:
<article data-sly-use.model="com.mysite.models.ArticleModel">
<header>
<h1>${model.title}</h1>
<time datetime="${model.publishDate}">${model.formattedDate}</time>
<p class="author">by ${model.authorName}</p>
</header>
<div class="content">
${model.content }
</div>
<footer>
<ul class="tags" data-sly-list.tag="${model.tags}">
<li>${tag}</li>
</ul>
</footer>
</article>2. Service Injection
Inject AEM services in the Model:
@Model(adaptables = {Resource.class, SlingHttpServletRequest.class})
public class NavigationModel {
@Inject
private PageManager pageManager;
@Inject
private Page currentPage;
@SlingObject
private ResourceResolver resourceResolver;
public List<NavigationItem> getNavigationItems() {
// Use pageManager to navigate
Page rootPage = currentPage.getAbsoluteParent(2);
List<NavigationItem> items = new ArrayList<>();
Iterator<Page> children = rootPage.listChildren();
while (children.hasNext()) {
Page child = children.next();
if (!child.isHideInNav()) {
items.add(new NavigationItem(child.getTitle(), child.getPath()));
}
}
return items;
}
}HTL:
<nav data-sly-use.nav="com.mysite.models.NavigationModel">
<ul>
<li data-sly-list.item="${nav.navigationItems}">
<a href="${item.path}">${item.title}</a>
</li>
</ul>
</nav>3. Multiple data-sly-use
You can use multiple models in the same template:
<div data-sly-use.page="com.example.PageModel"
data-sly-use.user="com.example.UserModel"
data-sly-use.config="com.example.ConfigModel">
<h1>${page.title}</h1>
<p>Welcome, ${user.name}</p>
<p>Language: ${config.locale}</p>
</div>Java vs JavaScript Differences
| Aspect | Java (Sling Model) | JavaScript |
|---|---|---|
| Performance | ✓ Faster (compiled) | Slower (interpreted) |
| Type Safety | ✓ Strongly typed | Weakly typed |
| IDE Support | ✓ Excellent (autocomplete, refactoring) | Limited |
| Testing | ✓ Complete JUnit tests | More difficult |
| Injection | ✓ @Inject, @ValueMapValue | Limited |
| Services | ✓ Access to all AEM services | Only resource access |
| Best For | Complex logic, enterprise | Simple scripts, prototypes |
Recommendation: Use Java Sling Models for enterprise projects!
Complete Example: Blog Post
Java Model
@Model(adaptables = {Resource.class, SlingHttpServletRequest.class})
public class BlogPostModel {
@Inject
private Page currentPage;
@ValueMapValue
private String title;
@ValueMapValue
private Calendar publishDate;
@ValueMapValue
private String[] tags;
@ValueMapValue
private String content;
public String getTitle() {
return title != null ? title : currentPage.getTitle();
}
public String getFormattedDate() {
if (publishDate == null) return "";
SimpleDateFormat sdf = new SimpleDateFormat("dd MMMM yyyy", Locale.ENGLISH);
return sdf.format(publishDate.getTime());
}
public List<String> getTags() {
return tags != null ? Arrays.asList(tags) : Collections.emptyList();
}
public String getExcerpt() {
if (content == null) return "";
String plainText = content.replaceAll("<[^>]*>", "");
return plainText.length() > 200 ? plainText.substring(0, 200) + "..." : plainText;
}
public int getReadingTime() {
if (content == null) return 0;
String plainText = content.replaceAll("<[^>]*>", "");
int wordCount = plainText.split("\\s+").length;
return Math.max(1, wordCount / 200); // 200 words per minute
}
public boolean hasTags() {
return tags != null && tags.length > 0;
}
}HTL Template
<article data-sly-use.post="com.mysite.models.BlogPostModel" class="blog-post">
<!-- Header -->
<header class="post-header">
<h1>${post.title}</h1>
<div class="post-meta">
<time datetime="${post.publishDate}">${post.formattedDate}</time>
<span class="dot">•</span>
<span>${post.readingTime} min read</span>
</div>
</header>
<!-- Content -->
<div class="post-content">
${post.content }
</div>
<!-- Tags -->
<footer data-sly-test="${post.hasTags}" class="post-footer">
<h3>Tags:</h3>
<ul class="tag-list">
<li data-sly-list.tag="${post.tags}">
<a href="/blog/tag/${tag}">${tag}</a>
</li>
</ul>
</footer>
</article>Best Practices
- Prefer Java Sling Models for production projects
- Separate logic from presentation: complex calculations in the model
- Use at the beginning: load models in the root element when possible
- Clear naming:
author,product,configinstead ofmodel,data - Injection over properties: use
@Injectinstead ofproperties.get() - Unit test models: test logic separately
- Documentation: comment public methods
- Null safety: handle null/empty in getters
Common Mistakes
❌ Wrong Java Path
<!-- WRONG - typo in package -->
<div data-sly-use.model="com.exampel.Model">
<!-- CORRECT -->
<div data-sly-use.model="com.example.Model">❌ Access Out of Scope
<div data-sly-use.model="com.example.Model">
${model.title}
</div>
${model.title} <!-- ERROR! -->❌ Don't Override properties
// ❌ WRONG - shadows global properties
public String getProperties() {
return "something";
}
// ✓ CORRECT - different name
public Map<String, Object> getComponentProperties() {
return componentProps;
}Practical Exercises
- User Profile Model: Create model with avatar, bio, social links
- Product Catalog: Model with filtering and sorting
- Navigation Builder: Generate menu from page tree
Next Lesson
In the next lesson we'll cover data-sly-template and data-sly-call to create reusable templates and macros in HTL.
Lesson #8 of the HTL Tutorial series. ← Previous lesson | Next lesson →