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

  1. Use <sly> for templates: doesn't add unnecessary markup
  2. Descriptive names: userCard, alertBox instead of template1
  3. Documentation: comment the expected parameters
  4. External files for shared templates: /apps/mysite/components/templates/
  5. Parameters with defaults: use ${param || 'default'}
  6. Don't overdo it: overly complex templates are hard to maintain
  7. 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

  1. Alert System: Template for alerts (success, error, warning, info)
  2. Navigation Menu: Template for menu items with icons and badges
  3. Table Rows: Reusable template for table rows
  4. 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 →