Aller au contenu
AEM 7 min de lecture

AEM : développer des composants avec HTL et Sling

Guide pratique pour créer des composants Adobe Experience Manager avec HTL (Sightly) et les Sling Models en Java — architecture, bonnes pratiques et exemples concrets.

AEMHTLSlingJavaOSGi

Adobe Experience Manager repose sur deux piliers techniques pour le développement de composants : HTL (HTML Template Language, anciennement Sightly) côté template, et Sling Models côté logique Java. Comprendre comment ils s’articulent est indispensable pour produire un code propre, maintenable et conforme aux standards Adobe.

Architecture d’un composant AEM

Un composant AEM est un nœud JCR de type cq:Component. Sa structure minimale contient :

/apps/monprojet/components/moncomposant/
  .content.xml        ← définition du nœud JCR
  moncomposant.html   ← template HTL
  _cq_dialog/         ← dialog de configuration auteur
  models/
    MoncomposantModel.java ← Sling Model

Le contenu du composant est stocké dans le JCR et lu via Sling. La logique métier est encapsulée dans un Sling Model, jamais dans le template HTL.

HTL : un template sans logique

HTL est intentionnellement limité : pas de boucles complexes, pas d’expressions arbitraires. C’est une contrainte volontaire pour forcer la séparation des responsabilités.

<!-- moncomposant.html -->
<sly data-sly-use.model="com.monprojet.models.MoncomposantModel" />

<div class="mon-composant" data-sly-test="${model.visible}">
  <h2>${model.titre @ context='html'}</h2>
  <p>${model.description @ context='html'}</p>

  <ul data-sly-list.item="${model.liens}">
    <li><a href="${item.url}">${item.label}</a></li>
  </ul>
</div>

Points clés :

  • data-sly-use instancie le Sling Model
  • data-sly-test conditionne l’affichage
  • data-sly-list itère sur une collection
  • @ context='html' permet le rendu HTML (à utiliser avec précaution)

Sling Models : la logique en Java

Le Sling Model est un bean Java annoté qui lit le contenu depuis le JCR et expose des méthodes au template.

@Model(
    adaptables = SlingHttpServletRequest.class,
    adapters = MoncomposantModel.class,
    defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
public class MoncomposantModel {

    @ValueMapValue
    private String titre;

    @ValueMapValue
    private String description;

    @ChildResource
    private List<LinkItem> liens;

    @SlingObject
    private Resource resource;

    public boolean isVisible() {
        return StringUtils.isNotBlank(titre);
    }

    public String getTitre() {
        return titre;
    }

    public String getDescription() {
        return description;
    }

    public List<LinkItem> getLiens() {
        return liens != null ? liens : Collections.emptyList();
    }
}

@ValueMapValue injecte directement la propriété JCR correspondante au nom du champ. @ChildResource injecte un nœud enfant. @SlingObject donne accès à l’objet Sling.

La dialog auteur (_cq_dialog)

La dialog est ce que voit le rédacteur dans l’interface auteur d’AEM. Elle est définie en XML et repose sur des widgets Granite UI.

<!-- _cq_dialog/.content.xml -->
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
          xmlns:jcr="http://www.jcp.org/jcr/1.0"
          jcr:primaryType="nt:unstructured"
          jcr:title="Mon Composant"
          sling:resourceType="cq/gui/components/authoring/dialog">
  <content jcr:primaryType="nt:unstructured"
           sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns">
    <items jcr:primaryType="nt:unstructured">
      <column jcr:primaryType="nt:unstructured"
              sling:resourceType="granite/ui/components/coral/foundation/container">
        <items jcr:primaryType="nt:unstructured">
          <titre jcr:primaryType="nt:unstructured"
                 sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                 fieldLabel="Titre"
                 name="./titre" />
        </items>
      </column>
    </items>
  </content>
</jcr:root>

Bonnes pratiques

Séparation stricte : aucune logique dans HTL, aucun HTML dans le Sling Model. Le model expose des données, le template les affiche.

DefaultInjectionStrategy.OPTIONAL : évite les NullPointerException si une propriété JCR n’existe pas. Gérez les cas null dans le model.

Tester les models en isolation : les Sling Models sont des POJOs testables avec JUnit, sans démarrer AEM. Utilisez SlingContext de io.wcm.testing.mock.aem.

Versionner les dialogs : une modification de dialog sans migration de contenu peut casser les composants existants en production.

Conclusion

HTL et Sling Models forment un duo solide pour développer sur AEM. HTL garantit des templates lisibles et sécurisés, les Sling Models centralisent la logique et rendent le code testable. Maîtriser ce duo, c’est écrire du code AEM qui tient dans le temps — même quand une autre équipe doit le reprendre.

Amine MEGDICHE

Amine MEGDICHE

Développeur AEM & Java Full Stack — Freelance depuis 2013

Vous avez un projet sur ces sujets ?

Envoyez-moi un message pour qualifier votre besoin, vos contraintes techniques et le bon format d'intervention.

Cadrer mon besoin