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 @ context='html'}
  </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 @ context='html'}
  </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

  1. Prefer Java Sling Models for production projects
  2. Separate logic from presentation: complex calculations in the model
  3. Use at the beginning: load models in the root element when possible
  4. Clear naming: author, product, config instead of model, data
  5. Injection over properties: use @Inject instead of properties.get()
  6. Unit test models: test logic separately
  7. Documentation: comment public methods
  8. 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

  1. User Profile Model: Create model with avatar, bio, social links
  2. Product Catalog: Model with filtering and sorting
  3. 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 →