HTL Tutorial #7: data-sly-list - Iterazione su Array e Collezioni

HTL Tutorial #7: data-sly-list - Iterazione su Array e Collezioni

Cos'è data-sly-list?

data-sly-list itera su array, liste o collezioni e ripete l'elemento per ogni item.

Sintassi Base

<elemento data-sly-list="${array}">
  <!-- Questo elemento viene ripetuto per ogni item -->
</elemento>

Con Nome Variabile

<elemento data-sly-list.nomeItem="${array}">
  ${nomeItem}
</elemento>

Esempio Semplice

<!-- Array di stringhe -->
<ul data-sly-list.tag="${properties.tags}">
  <li>${tag}</li>
</ul>

Input: properties.tags = ["HTML", "CSS", "JavaScript"]

Output:

<ul>
  <li>HTML</li>
  <li>CSS</li>
  <li>JavaScript</li>
</ul>

Senza Nome Variabile

Se non specifichi un nome, HTL usa automaticamente item:

<!-- Senza nome -->
<ul data-sly-list="${properties.tags}">
  <li>${item}</li>
</ul>

<!-- Equivalente a -->
<ul data-sly-list.item="${properties.tags}">
  <li>${item}</li>
</ul>

Iterare su Array di Oggetti

Esempio: Lista Prodotti

<div class="products" data-sly-use.model="com.example.ProductsModel">
  <div data-sly-list.product="${model.products}" class="product-card">
    <h3>${product.name}</h3>
    <p class="price">${product.price}</p>
    <p>${product.description}</p>
    <button>Aggiungi al carrello</button>
  </div>
</div>

Java Model (esempio):

public class ProductsModel {
    public List<Product> getProducts() {
        return Arrays.asList(
            new Product("Laptop", 999.00, "Laptop potente"),
            new Product("Mouse", 29.99, "Mouse wireless"),
            new Product("Tastiera", 79.99, "Tastiera meccanica")
        );
    }
}

Output:

<div class="products">
  <div class="product-card">
    <h3>Laptop</h3>
    <p class="price">€ 999.0</p>
    <p>Laptop potente</p>
    <button>Aggiungi al carrello</button>
  </div>
  <div class="product-card">
    <h3>Mouse</h3>
    <p class="price">€ 29.99</p>
    <p>Mouse wireless</p>
    <button>Aggiungi al carrello</button>
  </div>
  <div class="product-card">
    <h3>Tastiera</h3>
    <p class="price">€ 79.99</p>
    <p>Tastiera meccanica</p>
    <button>Aggiungi al carrello</button>
  </div>
</div>

Variabile itemList

HTL fornisce automaticamente la variabile itemList con informazioni sull'iterazione:

<ul data-sly-list.item="${properties.items}">
  <li>
    ${item}
    (${itemList.index + 1}/${itemList.count})
  </li>
</ul>

Proprietà di itemList

Proprietà Descrizione Tipo
index Indice corrente (inizia da 0) Number
count Numero totale di elementi Number
first True se è il primo elemento Boolean
last True se è l'ultimo elemento Boolean
odd True se indice dispari Boolean
even True se indice pari Boolean
middle True se NON è primo né ultimo Boolean

Esempio completo:

<ul data-sly-list.item="${properties.navItems}">
  <li class="${itemList.first ? 'first' : ''}
              ${itemList.last ? 'last' : ''}
              ${itemList.odd ? 'odd' : 'even'}">
    ${itemList.index + 1}. ${item.title}
  </li>
</ul>

Pattern Comuni

1. Lista con Indice

<ol data-sly-list.step="${properties.steps}">
  <li>
    <strong>Passo ${stepList.index + 1}:</strong>
    ${step}
  </li>
</ol>

2. Zebra Striping (Righe Alternate)

<table>
  <tbody data-sly-list.row="${model.data}">
    <tr class="${rowList.odd ? 'bg-light' : 'bg-white'}">
      <td>${row.name}</td>
      <td>${row.value}</td>
    </tr>
  </tbody>
</table>

3. Primo e Ultimo Diversi

<nav>
  <ul data-sly-list.page="${model.pages}">
    <li>
      <a href="${page.url}"
         class="${pageList.first ? 'home-link' : ''}
                ${pageList.last ? 'last-link' : ''}">
        ${page.title}
      </a>
    </li>
  </ul>
</nav>

4. Separatori tra Elementi

<div class="breadcrumb">
  <span data-sly-list.crumb="${model.breadcrumbs}">
    <a href="${crumb.url}">${crumb.title}</a>
    <span data-sly-test="${!crumbList.last}"> &gt; </span>
  </span>
</div>

Output: Home > Products > Category > Item

5. Primi N Elementi

<!-- Mostra solo i primi 5 -->
<ul data-sly-list.item="${properties.items}">
  <li data-sly-test="${itemList.index < 5}">
    ${item}
  </li>
</ul>

Liste Annidate

Puoi annidare data-sly-list per strutture complesse:

<div data-sly-use.nav="com.example.NavigationModel">
  <!-- Menu principale -->
  <nav>
    <ul data-sly-list.section="${nav.sections}">
      <li>
        <h3>${section.title}</h3>

        <!-- Sottomenu -->
        <ul data-sly-list.item="${section.items}">
          <li>
            <a href="${item.url}">${item.title}</a>
          </li>
        </ul>
      </li>
    </ul>
  </nav>
</div>

Combinazione con data-sly-test

Lista Condizionale

<!-- Mostra lista solo se ha elementi -->
<ul data-sly-test="${properties.tags}"
    data-sly-list.tag="${properties.tags}">
  <li>${tag}</li>
</ul>

<!-- Messaggio se vuota -->
<p data-sly-test="${!properties.tags}">
  Nessun tag disponibile
</p>

Filtraggio Elementi

<ul data-sly-list.user="${model.users}">
  <!-- Mostra solo utenti attivi -->
  <li data-sly-test="${user.active}">
    ${user.name} (${user.email})
  </li>
</ul>

Esempi Pratici Completi

Esempio 1: Gallery Immagini

<div class="gallery" data-sly-use.gallery="com.example.GalleryModel">
  <div class="grid">
    <div data-sly-list.image="${gallery.images}" class="grid-item">
      <img src="${image.url}"
           alt="${image.alt}"
           loading="${imageList.first ? 'eager' : 'lazy'}">

      <div class="caption">
        <h4>${image.title}</h4>
        <p>${image.description}</p>
        <small>Immagine ${imageList.index + 1} di ${imageList.count}</small>
      </div>
    </div>
  </div>
</div>

Esempio 2: Timeline Eventi

<div class="timeline" data-sly-use.events="com.example.EventsModel">
  <div data-sly-list.event="${events.items}"
       class="timeline-item ${eventList.odd ? 'left' : 'right'}">

    <!-- Marker timeline -->
    <div class="timeline-marker ${eventList.first ? 'first' : ''}
                                  ${eventList.last ? 'last' : ''}">
      ${eventList.index + 1}
    </div>

    <!-- Contenuto evento -->
    <div class="timeline-content">
      <h3>${event.title}</h3>
      <time datetime="${event.date}">${event.formattedDate}</time>
      <p>${event.description}</p>

      <!-- Badge per primo evento -->
      <span data-sly-test="${eventList.first}" class="badge-latest">
        Ultimo evento
      </span>
    </div>
  </div>
</div>

Esempio 3: Table con Totale

<table data-sly-use.report="com.example.ReportModel">
  <thead>
    <tr>
      <th>#</th>
      <th>Descrizione</th>
      <th>Quantità</th>
      <th>Prezzo</th>
    </tr>
  </thead>
  <tbody>
    <tr data-sly-list.item="${report.items}">
      <td>${itemList.index + 1}</td>
      <td>${item.description}</td>
      <td>${item.quantity}</td>
      <td>${item.price}</td>
    </tr>
  </tbody>
  <tfoot>
    <tr>
      <td colspan="3"><strong>Totale</strong></td>
      <td><strong>${report.total}</strong></td>
    </tr>
  </tfoot>
</table>

Esempio 4: Card Grid Responsive

<div class="card-grid" data-sly-use.blog="com.example.BlogModel">
  <article data-sly-list.post="${blog.recentPosts}"
           class="card ${postList.first ? 'featured' : ''}">

    <!-- Immagine -->
    <div class="card-image">
      <img src="${post.thumbnail}"
           alt="${post.title}"
           loading="${postList.index < 3 ? 'eager' : 'lazy'}">

      <!-- Badge Featured solo per il primo -->
      <span data-sly-test="${postList.first}" class="badge-featured">
        In Evidenza
      </span>
    </div>

    <!-- Contenuto -->
    <div class="card-body">
      <h3>${post.title}</h3>
      <p class="excerpt">${post.excerpt}</p>

      <!-- Meta info -->
      <div class="meta">
        <time datetime="${post.date}">${post.formattedDate}</time>
        <span class="dot"></span>
        <span>${post.readTime} min lettura</span>
      </div>

      <a href="${post.url}" class="btn">Leggi di più</a>
    </div>
  </article>
</div>

data-sly-list vs data-sly-repeat

HTL ha due statement per iterazione:

  • data-sly-list: Più semplice, usa itemList
  • data-sly-repeat: Più dettagliato, vedremo nella prossima lezione

Per la maggior parte dei casi, data-sly-list è sufficiente!

Liste Vuote

Se l'array è vuoto/null, l'elemento NON viene renderizzato:

<ul data-sly-list.tag="${properties.tags}">
  <li>${tag}</li>
</ul>

<!-- Se tags è vuoto: NESSUN output (nemmeno <ul>) -->

Gestire liste vuote:

<div data-sly-test.hasTags="${properties.tags}">
  <h3>Tags:</h3>
  <ul data-sly-list.tag="${properties.tags}">
    <li>${tag}</li>
  </ul>
</div>

<p data-sly-test="${!hasTags}">
  Nessun tag disponibile
</p>

Best Practice

  1. Usa nomi variabili descrittivi: product, user, image invece di item
  2. Sfrutta itemList.first/last per styling speciale
  3. Combina con data-sly-test per filtraggio
  4. Lazy loading per immagini non immediate: ${!imageList.first}
  5. Gestisci liste vuote con data-sly-test separato
  6. Evita logica complessa nel loop - spostala nel Sling Model

Errori Comuni

❌ Modificare l'array durante l'iterazione

<!-- NON supportato - read-only! -->
<ul data-sly-list.item="${items}">
  <li>${item}</li>
</ul>

❌ Usare variabile fuori scope

<ul data-sly-list.tag="${properties.tags}">
  <li>${tag}</li>
</ul>

${tag} <!-- ERRORE - fuori scope! -->

Esercizi Pratici

  1. Navigation Menu: Lista con prima/ultima pagina diversa
  2. Product Grid: 3 colonne con righe zebrate
  3. Comments: Lista commenti con separatori
  4. FAQ Accordion: Domande/risposte con primi 3 elementi aperti di default

Prossima Lezione

Nella prossima lezione scopriremo data-sly-repeat, la versione avanzata di list con più informazioni di iterazione disponibili.


Lezione #7 della serie HTL Tutorial. ← Lezione precedente | Lezione successiva →