HTL Tutorial #9: data-sly-template and data-sly-call - Reusable Templates
HTL Tutorial #9: data-sly-template and data-sly-call - Reusable Templates
What is an HTL Template?
A template is a reusable block of markup that you can invoke multiple times with different parameters.
Think of templates as functions in programming!
Syntax
Defining a Template
<sly data-sly-template.templateName="${@ param1, param2}">
<!-- template markup -->
${param1}
${param2}
</sly>Calling a Template
<sly data-sly-call="${templateName @ param1='value1', param2='value2'}"></sly>Basic Example
<!-- Template definition -->
<sly data-sly-template.userCard="${@ name, email}">
<div class="user-card">
<h3>${name}</h3>
<p>${email}</p>
</div>
</sly>
<!-- Template usage -->
<sly data-sly-call="${userCard @ name='Mario', email='mario@example.com'}"></sly>
<sly data-sly-call="${userCard @ name='Luigi', email='luigi@example.com'}"></sly>Output:
<div class="user-card">
<h3>Mario</h3>
<p>mario@example.com</p>
</div>
<div class="user-card">
<h3>Luigi</h3>
<p>luigi@example.com</p>
</div>Why Use Templates?
1. DRY (Don't Repeat Yourself)
Without templates (repetitive):
<div class="alert alert-success">
<strong>Success!</strong> Operation completed
</div>
<div class="alert alert-danger">
<strong>Error!</strong> Something went wrong
</div>
<div class="alert alert-warning">
<strong>Warning!</strong> Check your data
</div>With templates (reusable):
<!-- Definition -->
<sly data-sly-template.alert="${@ type, title, message}">
<div class="alert alert-${type}">
<strong>${title}</strong> ${message}
</div>
</sly>
<!-- Usage -->
<sly data-sly-call="${alert @ type='success', title='Success!', message='Operation completed'}"></sly>
<sly data-sly-call="${alert @ type='danger', title='Error!', message='Something went wrong'}"></sly>
<sly data-sly-call="${alert @ type='warning', title='Warning!', message='Check your data'}"></sly>2. Maintainability
Modify the template once, the change applies everywhere!
3. Consistency
All uses of the template have the same structure.
Templates with Lists
You can combine templates with data-sly-list:
<!-- Template for single product -->
<sly data-sly-template.productCard="${@ product}">
<div class="product-card">
<img src="${product.image}" alt="${product.name}">
<h3>${product.name}</h3>
<p class="price">€ ${product.price}</p>
<button>Add to cart</button>
</div>
</sly>
<!-- Usage with list -->
<div data-sly-use.model="com.example.ProductsModel" class="product-grid">
<sly data-sly-list.product="${model.products}"
data-sly-call="${productCard @ product=product}"></sly>
</div>Conditional Templates
<!-- Template for different badge types -->
<sly data-sly-template.badge="${@ type, text}">
<span class="badge badge-${type}">
${text}
</span>
</sly>
<!-- Conditional usage -->
<div data-sly-use.user="com.example.UserModel">
<sly data-sly-test="${user.isPremium}"
data-sly-call="${badge @ type='gold', text='Premium'}"></sly>
<sly data-sly-test="${user.isModerator}"
data-sly-call="${badge @ type='blue', text='Moderator'}"></sly>
<sly data-sly-test="${user.isAdmin}"
data-sly-call="${badge @ type='red', text='Admin'}"></sly>
</div>Nested Templates
You can define templates inside templates:
<!-- Main template -->
<sly data-sly-template.articleCard="${@ article}">
<!-- Internal template for meta info -->
<sly data-sly-template.metaInfo="${@ date, author}">
<div class="meta">
<time>${date}</time>
<span>•</span>
<span>${author}</span>
</div>
</sly>
<!-- Use template -->
<article class="card">
<h3>${article.title}</h3>
<p>${article.excerpt}</p>
<sly data-sly-call="${metaInfo @ date=article.date, author=article.author}"></sly>
</article>
</sly>
<!-- Usage -->
<sly data-sly-call="${articleCard @ article=myArticle}"></sly>Templates in External Files
You can define templates in separate files and include them:
File: /apps/mysite/components/templates/cards.html
<sly data-sly-template.userCard="${@ user}">
<div class="user-card">
<h3>${user.name}</h3>
<p>${user.email}</p>
</div>
</sly>
<sly data-sly-template.productCard="${@ product}">
<div class="product-card">
<h3>${product.name}</h3>
<p>${product.price}</p>
</div>
</sly>Usage in component:
<div data-sly-use.templates="/apps/mysite/components/templates/cards.html">
<sly data-sly-call="${templates.userCard @ user=currentUser}"></sly>
<sly data-sly-call="${templates.productCard @ product=featuredProduct}"></sly>
</div>Common Patterns
1. Button Component
<sly data-sly-template.button="${@ text, url, style}">
<a href="${url}" class="btn btn-${style || 'primary'}">
${text}
</a>
</sly>
<!-- Usage -->
<sly data-sly-call="${button @ text='Learn more', url='/about', style='secondary'}"></sly>
<sly data-sly-call="${button @ text='Contact us', url='/contact'}"></sly>2. Icon with Text
<sly data-sly-template.iconText="${@ icon, text}">
<span class="icon-text">
<i class="icon-${icon}"></i>
<span>${text}</span>
</span>
</sly>
<!-- Usage -->
<sly data-sly-call="${iconText @ icon='calendar', text='Jan 15, 2025'}"></sly>
<sly data-sly-call="${iconText @ icon='user', text='Mario Rossi'}"></sly>
<sly data-sly-call="${iconText @ icon='tag', text='Tutorial'}"></sly>3. Social Share Buttons
<sly data-sly-template.socialButton="${@ network, url, title}">
<a href="https://${network}.com/share?url=${url}&title=${title}"
class="social-btn social-${network}"
target="_blank"
rel="noopener">
<i class="icon-${network}"></i>
Share on ${network}
</a>
</sly>
<!-- Usage -->
<div class="social-share">
<sly data-sly-call="${socialButton @ network='facebook', url=currentPage.path, title=currentPage.title}"></sly>
<sly data-sly-call="${socialButton @ network='twitter', url=currentPage.path, title=currentPage.title}"></sly>
<sly data-sly-call="${socialButton @ network='linkedin', url=currentPage.path, title=currentPage.title}"></sly>
</div>Complete Example: Card System
<!-- ============================================
TEMPLATE DEFINITION
============================================ -->
<!-- Base card template -->
<sly data-sly-template.baseCard="${@ title, content, footer}">
<div class="card">
<div class="card-header">
<h3>${title}</h3>
</div>
<div class="card-body">
${content}
</div>
<div data-sly-test="${footer}" class="card-footer">
${footer}
</div>
</div>
</sly>
<!-- User card template -->
<sly data-sly-template.userCard="${@ user}">
<sly data-sly-call="${baseCard @
title=user.name,
content=user.bio,
footer=user.email}">
</sly>
</sly>
<!-- Product card template -->
<sly data-sly-template.productCard="${@ product}">
<sly data-sly-call="${baseCard @
title=product.name,
content=product.description,
footer='€ ' + product.price}">
</sly>
</sly>
<!-- ============================================
TEMPLATE USAGE
============================================ -->
<div data-sly-use.model="com.example.DashboardModel">
<!-- Users section -->
<section>
<h2>Users</h2>
<div class="grid">
<sly data-sly-list.user="${model.users}"
data-sly-call="${userCard @ user=user}"></sly>
</div>
</section>
<!-- Products section -->
<section>
<h2>Featured Products</h2>
<div class="grid">
<sly data-sly-list.product="${model.products}"
data-sly-call="${productCard @ product=product}"></sly>
</div>
</section>
</div>Optional Parameters
You can have optional parameters using the OR operator:
<sly data-sly-template.alert="${@ message, type, icon}">
<div class="alert alert-${type || 'info'}">
<i data-sly-test="${icon}" class="icon-${icon}"></i>
${message}
</div>
</sly>
<!-- With all parameters -->
<sly data-sly-call="${alert @ message='Success!', type='success', icon='check'}"></sly>
<!-- With optional parameters omitted -->
<sly data-sly-call="${alert @ message='General info'}"></sly>
<!-- Uses type='info' and no icon -->Best Practices
- Use
<sly>for templates: doesn't add unnecessary markup - Descriptive names:
userCard,alertBoxinstead oftemplate1 - Documentation: comment the expected parameters
- External files for shared templates:
/apps/mysite/components/templates/ - Parameters with defaults: use
${param || 'default'} - Don't overdo it: overly complex templates are hard to maintain
- Limited scope: define templates close to their usage
Template vs Include
| Feature | Template | Include |
|---|---|---|
| Parameters | ✓ Supported | ❌ No |
| Reusability | ✓ Multiple calls | ✓ Include once |
| Scope | Local to file | New scope |
| Performance | ✓ Lightweight | Includes separate file |
| Use Case | Repeated patterns | Common components |
When to use what:
- Template: Repeated patterns in the same file (cards, buttons, badges)
- Include: Complete components (header, footer, navigation)
Common Mistakes
❌ Undeclared parameters
<!-- WRONG - 'name' not declared -->
<sly data-sly-template.user="${@ email}">
${name} <!-- ERROR! -->
</sly>
<!-- CORRECT -->
<sly data-sly-template.user="${@ name, email}">
${name}
</sly>❌ Call before definition
<!-- WRONG - template not yet defined -->
<sly data-sly-call="${myTemplate}"></sly>
<sly data-sly-template.myTemplate="${@}">
<!-- ... -->
</sly>
<!-- CORRECT - define first -->
<sly data-sly-template.myTemplate="${@}">
<!-- ... -->
</sly>
<sly data-sly-call="${myTemplate}"></sly>❌ Overly complex templates
<!-- WRONG - too complex -->
<sly data-sly-template.megaTemplate="${@ a, b, c, d, e, f, g}">
<!-- 100 lines of code -->
</sly>
<!-- CORRECT - split into multiple templates -->
<sly data-sly-template.header="${@ title}">...</sly>
<sly data-sly-template.body="${@ content}">...</sly>
<sly data-sly-template.footer="${@ meta}">...</sly>Practical Exercises
- Alert System: Template for alerts (success, error, warning, info)
- Navigation Menu: Template for menu items with icons and badges
- Table Rows: Reusable template for table rows
- Media Object: Template for objects with image + text
Next Lesson
In the next lesson we'll see data-sly-attribute for dynamically manipulating HTML attributes.
Lesson #9 of the HTL Tutorial series. ← Previous lesson | Next lesson →