Custom Component (composant personnalisé) custom-component
Ce tutoriel décrit la création de bout en bout d’un composant AEM de Byline
personnalisé qui affiche le contenu créé dans une boîte de dialogue et explore le développement d’un modèle Sling pour encapsuler la logique commerciale qui renseigne le HTL du composant.
Prérequis prerequisites
Examinez les outils et les instructions nécessaires pour configurer un environnement de développement local.
Projet de démarrage
Consultez le code de ligne de base sur lequel le tutoriel s’appuie :
-
Consultez la branche
tutorial/custom-component-start
à partir de GitHub.code language-shell $ cd aem-guides-wknd $ git checkout tutorial/custom-component-start
-
Déployez la base de code sur une instance locale d’AEM à l’aide de vos compétences Maven :
code language-shell $ mvn clean install -PautoInstallSinglePackage
note note NOTE Si vous utilisez AEM 6.5 ou 6.4, ajoutez le profil classic
à n’importe quelle commande Maven.code language-shell $ mvn clean install -PautoInstallSinglePackage -Pclassic
Vous pouvez toujours afficher le code terminé sur GitHub ou consulter le code localement en passant à la branche tutorial/custom-component-solution
.
Objectif
- Comprendre comment créer un composant AEM personnalisé
- Découvrir comment encapsuler la logique commerciale avec les modèles Sling
- Comprendre comment utiliser un modèle Sling dans un script HTL
Ce que vous allez créer what-build
Dans cette partie du tutoriel WKND, un composant de signature est créé afin d’afficher les informations créées sur le contributeur ou la contributrice d’un article.
Composant de signature
L’implémentation du composant de signature comprend une boîte de dialogue qui collecte le contenu de la signature et un modèle Sling personnalisé qui récupère les détails tels que :
- Nom
- Image
- Professions
Créer un composant de signature create-byline-component
Créez d’abord la structure de nœud du composant de signature et définissez une boîte de dialogue. Cela représente le composant dans AEM et définit implicitement le type de ressource du composant en fonction de son emplacement dans le JCR.
La boîte de dialogue expose l’interface que les auteurs et autrices de contenu peuvent fournir. Pour cette implémentation, le composant Image du composant principal de la gestion du contenu web AEM est utilisé pour gérer la création et le rendu de l’image de la signature. Il doit donc être défini comme le sling:resourceSuperType
de ce composant.
Créer une définition de composant create-component-definition
-
Dans le module ui.apps, accédez à
/apps/wknd/components
et créez un dossier nommébyline
. -
Dans le dossier
byline
, ajoutez un fichier nommé.content.xml
. -
Remplissez le fichier
.content.xml
avec les éléments suivants :code language-xml <?xml version="1.0" encoding="UTF-8"?> <jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" jcr:primaryType="cq:Component" jcr:title="Byline" jcr:description="Displays a contributor's byline." componentGroup="WKND Sites Project - Content" sling:resourceSuperType="core/wcm/components/image/v2/image"/>
Le fichier XML ci-dessus fournit la définition du composant, y compris le titre, la description et le groupe.
sling:resourceSuperType
pointe verscore/wcm/components/image/v2/image
, qui est le composant Image principal.
Créer le script HTL create-the-htl-script
-
Dans le dossier
byline
, ajoutez un fichierbyline.html
, qui est responsable de la présentation HTML du composant. Il est important d’attribuer au fichier le même nom que le dossier, car il devient le script par défaut utilisé par Sling pour effectuer le rendu de ce type de ressource. -
Ajoutez le code suivant à
byline.html
.code language-html <!--/* byline.html */--> <div data-sly-use.placeholderTemplate="core/wcm/components/commons/v1/templates.html"> </div> <sly data-sly-call="${placeholderTemplate.placeholder @ isEmpty=true}"></sly>
Le fichier byline.html
est revu plus tard, une fois le modèle Sling créé. L’état actuel du fichier HTL permet au composant de s’afficher dans un état vide dans l’éditeur de page d’AEM Sites lorsqu’il est glissé-déposé sur la page.
Créer la définition de boîte de dialogue create-the-dialog-definition
Définissez ensuite une boîte de dialogue pour le composant Signature avec les champs suivants :
- Nom : champ de texte contenant le nom du contributeur.
- Image : référence à la photo de la biographie du contributeur ou de la contributrice.
- Fonctions : liste des fonctions attribuées au contributeur ou à la contributrice. Les fonctions doivent être classées par ordre alphabétique (de a à z).
-
Dans le dossier
byline
, créez un dossier appelé_cq_dialog
. -
Dans
byline/_cq_dialog
, ajoutez un fichier appelé.content.xml
. Il s’agit de la définition XML de la boîte de dialogue. Ajoutez le code XML suivant :code language-xml <?xml version="1.0" encoding="UTF-8"?> <jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" jcr:primaryType="nt:unstructured" jcr:title="Byline" sling:resourceType="cq/gui/components/authoring/dialog"> <content jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/container"> <items jcr:primaryType="nt:unstructured"> <tabs jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/tabs" maximized="{Boolean}false"> <items jcr:primaryType="nt:unstructured"> <asset jcr:primaryType="nt:unstructured" sling:hideResource="{Boolean}false"/> <metadata jcr:primaryType="nt:unstructured" sling:hideResource="{Boolean}true"/> <properties jcr:primaryType="nt:unstructured" jcr:title="Properties" sling:resourceType="granite/ui/components/coral/foundation/container" margin="{Boolean}true"> <items jcr:primaryType="nt:unstructured"> <columns jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns" margin="{Boolean}true"> <items jcr:primaryType="nt:unstructured"> <column jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/container"> <items jcr:primaryType="nt:unstructured"> <name jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/textfield" emptyText="Enter the contributor's name to display." fieldDescription="The contributor's name to display." fieldLabel="Name" name="./name" required="{Boolean}true"/> <occupations jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/multifield" fieldDescription="A list of the contributor's occupations." fieldLabel="Occupations" required="{Boolean}false"> <field jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/textfield" emptyText="Enter an occupation" name="./occupations"/> </occupations> </items> </column> </items> </columns> </items> </properties> </items> </tabs> </items> </content> </jcr:root>
Ces définitions de nœud de boîte de dialogue utilisent Sling Resourc Merger pour contrôler quels onglets de boîte de dialogue sont hérités du composant
sling:resourceSuperType
, dans ce cas, le composant Image des composants principaux.
Créer la boîte de dialogue Politique create-the-policy-dialog
En suivant la même approche que pour la création de la boîte de dialogue, créez une boîte de dialogue Politique (anciennement appelée Boîte de dialogue de conception) pour masquer les champs indésirables dans la configuration de la politique héritée du composant Image des composants principaux.
-
Dans le dossier
byline
, créez un dossier appelé_cq_design_dialog
. -
Dans
byline/_cq_design_dialog
, créez un fichier appelé.content.xml
. Mettez à jour le fichier avec le code XML ci-après. Il est plus facile d’ouvrir.content.xml
et de copier/coller le code XML ci-dessous dedans.code language-xml <?xml version="1.0" encoding="UTF-8"?> <jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" jcr:primaryType="nt:unstructured" jcr:title="Byline" sling:resourceType="cq/gui/components/authoring/dialog"> <content jcr:primaryType="nt:unstructured"> <items jcr:primaryType="nt:unstructured"> <tabs jcr:primaryType="nt:unstructured"> <items jcr:primaryType="nt:unstructured"> <properties jcr:primaryType="nt:unstructured"> <items jcr:primaryType="nt:unstructured"> <content jcr:primaryType="nt:unstructured"> <items jcr:primaryType="nt:unstructured"> <decorative jcr:primaryType="nt:unstructured" sling:hideResource="{Boolean}true"/> <altValueFromDAM jcr:primaryType="nt:unstructured" sling:hideResource="{Boolean}true"/> <titleValueFromDAM jcr:primaryType="nt:unstructured" sling:hideResource="{Boolean}true"/> <displayCaptionPopup jcr:primaryType="nt:unstructured" sling:hideResource="{Boolean}true"/> <disableUuidTracking jcr:primaryType="nt:unstructured" sling:hideResource="{Boolean}true"/> </items> </content> </items> </properties> <features jcr:primaryType="nt:unstructured"> <items jcr:primaryType="nt:unstructured"> <content jcr:primaryType="nt:unstructured"> <items jcr:primaryType="nt:unstructured"> <accordion jcr:primaryType="nt:unstructured"> <items jcr:primaryType="nt:unstructured"> <orientation jcr:primaryType="nt:unstructured" sling:hideResource="{Boolean}true"/> <crop jcr:primaryType="nt:unstructured" sling:hideResource="{Boolean}true"/> </items> </accordion> </items> </content> </items> </features> </items> </tabs> </items> </content> </jcr:root>
La base du code XML précédent de la boîte de dialogue Politique a été obtenue à partir du composant Image des composants principaux.
Comme dans la configuration de la boîte de dialogue, Sling Resource Merger est utilisé pour masquer les champs non pertinents qui sont autrement hérités de
sling:resourceSuperType
, comme le montrent les définitions de nœuds avec la propriétésling:hideResource="{Boolean}true"
.
Déployer le code deploy-the-code
-
Synchronisez les modifications dans
ui.apps
avec votre IDE ou en utilisant vos compétences Maven.
Ajouter le composant à une page add-the-component-to-a-page
Pour garder les choses simples et se concentrer sur le développement de composants AEM, ajoutons le composant Signature dans son état actuel à une page Article pour vérifier que la définition du nœud cq:Component
est correcte. Cela permet également de vérifier qu’AEM reconnaît la nouvelle définition de composant et que la boîte de dialogue du composant fonctionne pour la création.
Ajouter une image à AEM Assets
Tout d’abord, chargez un exemple de portrait dans AEM Assets pour remplir l’image dans le composant Signature.
-
Accédez au dossier LA Skateparks dans AEM Assets : http://localhost:4502/assets.html/content/dam/wknd/en/magazine/la-skateparks.
-
Chargez le portrait pour stacey-roswells.jpg dans le dossier.
Créer le composant author-the-component
Ajoutez ensuite le composant Signature à une page dans AEM. Comme le composant Signature est ajouté au groupe de composants Projet de sites WKND - Contenu via la définition ui.apps/src/main/content/jcr_root/apps/wknd/components/byline/.content.xml
, il est automatiquement disponible pour tous les conteneurs dont la politique autorise le groupe de composants Projet de sites WKND - Contenu. Il est donc disponible dans le Conteneur de dispositions de la page d’article.
-
Accédez à l’article LA Skatepark à l’adresse : http://localhost:4502/editor.html/content/wknd/us/en/magazine/guide-la-skateparks.html
-
Depuis la barre latérale gauche, effectuez un glisser-déposer d’un composant Signature au bas du Conteneur de dispositions de la page d’article ouverte.
-
Vérifiez que la barre latérale gauche est ouverte et visible et que l’outil de recherche de ressources** est sélectionné.
-
Sélectionnez l’espace réservé du composant Signature qui affiche la barre d’actions, puis appuyez sur l’icône représentant une clé à molette pour ouvrir la boîte de dialogue.
-
Une fois la boîte de dialogue ouverte et le premier onglet (Ressource) actif, ouvrez la barre latérale gauche. Depuis l’outil de recherche de ressources, faites glisser une image jusqu’à la zone de dépôt Image. Recherchez "stacey" pour trouver l’image de profil de Stacey Roswells fournie dans le package WKND ui.content.
-
Après avoir ajouté une image, cliquez sur l’onglet Propriétés pour saisir le Nom et les Professions.
Lorsque vous saisissez un emploi, entrez-les dans l’ordre alphabétique inverse afin que la logique de gestion de l’ordre alphabétique mise en œuvre dans le modèle Sling soit vérifiée.
Appuyez sur le bouton Terminé en bas à droite pour enregistrer les modifications.
Les créateurs et créatrices AEM configurent et créent des composants via les boîtes de dialogue. À ce stade, dans le développement du composant Signature, les boîtes de dialogue sont incluses pour la collecte des données, mais la logique pour rendre le contenu créé n’a pas encore été ajoutée. Par conséquent, seul l’espace réservé s’affiche.
-
Après avoir enregistré la boîte de dialogue, accédez à CRXDE Lite et passez en revue la manière dont le contenu du composant est stocké sur le nœud de contenu du composant Signature sous la page AEM.
Recherchez le nœud de contenu du composant Signature sous la page LA Skate Parks, c’est-à-dire
/content/wknd/us/en/magazine/guide-la-skateparks/jcr:content/root/container/container/byline
.Notez les noms des propriétés.
name
,occupations
, etfileReference
sont stockées sur le nœud Signature.Veuillez également noter que le code
sling:resourceType
du nœud est défini surwknd/components/content/byline
et lie ce nœud de contenu à l’implémentation du composant Signature.
Créer un modèle Sling de signature create-sling-model
Ensuite, nous allons créer un modèle Sling qui servira de modèle de données et abritera la logique commerciale du composant Signature.
Les modèles Sling sont des POJO Java™ axés sur les annotations (objets Java™ simples) qui facilitent le mappage des données du JCR aux variables Java™ et offrent une efficacité lors du développement dans le contexte AEM.
Vérifier les dépendances Maven maven-dependency
Le modèle Sling de signature repose sur plusieurs API Java™ fournies par AEM. Ces API sont mises à disposition via le code dependencies
répertorié dans le fichier POM du module core
. Le projet utilisé pour ce tutoriel a été créé pour AEM as a Cloud Service. Cependant, il est unique, car il est rétrocompatible avec AEM 6.5/6.4. Par conséquent, les deux dépendances pour Cloud Service et AEM 6.x sont incluses.
-
Ouvrez le fichier
pom.xml
sous<src>/aem-guides-wknd/core/pom.xml
. -
Recherchez la dépendance pour
aem-sdk-api
- AEM as a Cloud Service uniquementcode language-xml <dependency> <groupId>com.adobe.aem</groupId> <artifactId>aem-sdk-api</artifactId> </dependency>
Le aem-sdk-api contient toutes les API Java™ publiques exposées par AEM. Le
aem-sdk-api
est utilisé par défaut lors de la création de ce projet. La version est maintenue dans le fichier pom Reactor parent de la racine du projet àaem-guides-wknd/pom.xml
. -
Recherchez la dépendance du
uber-jar
- AEM 6.5/6.4 uniquementcode language-xml ... <dependency> <groupId>com.adobe.aem</groupId> <artifactId>uber-jar</artifactId> <classifier>apis</classifier> </dependency> ...
Le
uber-jar
n’est inclus que lorsque le profilclassic
est appelé, c’est-à-diremvn clean install -PautoInstallSinglePackage -Pclassic
. Encore une fois, cela ne concerne que ce projet. Dans un projet réel, généré à partir de l’archétype de projet AEM, leuber-jar
est la valeur par défaut si la version d’AEM spécifiée est 6.5 ou 6.4.Le uber-jar contient toutes les API Java™ publiques exposées par AEM 6.x. La version est maintenue dans le fichier pom Reactor parent de la racine du projet
aem-guides-wknd/pom.xml
. -
Recherchez la dépendance pour
core.wcm.components.core
:code language-xml <!-- Core Component Dependency --> <dependency> <groupId>com.adobe.cq</groupId> <artifactId>core.wcm.components.core</artifactId> </dependency>
Il s’agit de l’ensemble des API publiques Java™ exposées par les composants principaux AEM. Les composants principaux AEM sont un projet conservé en dehors d'AEM et ont donc un cycle de publication distinct. C’est pourquoi il s’agit d’une dépendance qui doit être incluse séparément et qui n'est pas incluse dans
uber-jar
ouaem-sdk-api
.Comme pour l’uber-jar, la version de cette dépendance est conservée dans le fichier pom Reactor parent de
aem-guides-wknd/pom.xml
.Plus loin dans ce tutoriel, la classe Image Core Component est utilisée pour afficher l’image dans le composant Signature. Il est nécessaire de disposer de la dépendance Core Component pour construire et compiler le modèle Sling.
Interface de signature byline-interface
Créez une interface Java™ publique pour la signature. Le Byline.java
définit les méthodes publiques nécessaires pour piloter le script HTL byline.html
.
-
À l’intérieur, le module
core
dans le dossiercore/src/main/java/com/adobe/aem/guides/wknd/core/models
crée un fichier nomméByline.java
. -
Mettre à jour
Byline.java
en utilisant les méthodes suivantes :code language-java package com.adobe.aem.guides.wknd.core.models; import java.util.List; /** * Represents the Byline AEM Component for the WKND Site project. **/ public interface Byline { /*** * @return a string to display as the name. */ String getName(); /*** * Occupations are to be sorted alphabetically in a descending order. * * @return a list of occupations. */ List<String> getOccupations(); /*** * @return a boolean if the component has enough content to display. */ boolean isEmpty(); }
Les deux premières méthodes exposent les valeurs des nom et professions pour le composant de signature.
La méthode
isEmpty()
est utilisée pour déterminer si le composant a du contenu à rendre ou s’il est en attente de configuration.Notez qu’il n’existe aucune méthode pour l’image ; ceci est vérifié ultérieurement.
-
Les versions des packages Java™ qui contiennent des classes Java™ publiques, dans ce cas un modèle Sling, doivent être gérées à l’aide du fichier
package-info.java
du package.Étant donné que le package Java™
com.adobe.aem.guides.wknd.core.models
de la source WKND déclare la version de1.0.0
, et qu’une interface publique et des méthodes avec des modifications mineures sont ajoutées, la version doit passer à1.1.0
. Ouvrez le fichier àcore/src/main/java/com/adobe/aem/guides/wknd/core/models/package-info.java
et modifiez@Version("1.0.0")
en@Version("2.1.0")
.code language-none @Version("2.1.0") package com.adobe.aem.guides.wknd.core.models; import org.osgi.annotation.versioning.Version;
Lorsqu’une modification est apportée aux fichiers de ce package, la version du package doit être ajustée sémantiquement. Si ce n’est pas le cas, le bnd-baseline-maven-plugin du projet Maven détecte une version de package non valide et rompt la version. Heureusement, en cas d’échec, le plug-in Maven signale la version non valide du package Java™ et la version devant être utilisée. Mettez à jour la déclaration @Version("...")
dans le package-info.java
du package Java™ en infraction à la version recommandée par le plug-in à corriger.
Implémentation par signature byline-implementation
Le BylineImpl.java
est l’implémentation du modèle Sling mettant en œuvre l’interface Byline.java
définie précédemment. Le code complet de BylineImpl.java
se trouve au bas de cette section.
-
Créez un dossier nommé
impl
souscore/src/main/java/com/adobe/aem/guides/core/models
. -
Dans le dossier
impl
, créez un fichierBylineImpl.java
. -
Ouvrez
BylineImpl.java
. Spécifiez qu’il implémente l’interfaceByline
. Utilisez les fonctionnalités de saisie semi-automatique de l’IDE ou mettez à jour manuellement le fichier afin d’inclure les méthodes nécessaires à l’implémentation de l’interfaceByline
:code language-java package com.adobe.aem.guides.wknd.core.models.impl; import java.util.List; import com.adobe.aem.guides.wknd.core.models.Byline; public class BylineImpl implements Byline { @Override public String getName() { // TODO Auto-generated method stub return null; } @Override public List<String> getOccupations() { // TODO Auto-generated method stub return null; } @Override public boolean isEmpty() { // TODO Auto-generated method stub return false; } }
-
Ajoutez des annotations de modèle Sling en mettant à jour
BylineImpl.java
avec les annotations au niveau de la classe suivantes. Cette annotation@Model(..)
transforme la classe en modèle Sling.code language-java import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.models.annotations.Model; import org.apache.sling.models.annotations.DefaultInjectionStrategy; ... @Model( adaptables = {SlingHttpServletRequest.class}, adapters = {Byline.class}, resourceType = {BylineImpl.RESOURCE_TYPE}, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL ) public class BylineImpl implements Byline { protected static final String RESOURCE_TYPE = "wknd/components/byline"; ... }
Examinons cette annotation et ses paramètres :
- L’annotation
@Model
enregistre BylineImpl en tant que modèle Sling lorsqu’il est déployé sur AEM. - Le paramètre
adaptables
indique que ce modèle peut être adapté par la requête. - Le paramètre
adapters
permet à la classe d’implémentation d’être enregistrée sous l’interface de signature. Cela permet au script HTL d’appeler le modèle Sling via l’interface (au lieu de l’implémentation directe). Vous trouverez plus d’informations sur les adaptateurs ici. - Le
resourceType
pointe vers le type de ressource du composant Signature (créé précédemment) et aide à résoudre le modèle correct s’il existe plusieurs mises en œuvre. Vous trouverez plus d’informations sur l’association d’une classe de modèle à un type de ressource ici.
- L’annotation
Implémentation des méthodes de modèle Sling implementing-the-sling-model-methods
getName() implementing-get-name
La première méthode implémentée est getName()
, qui renvoie simplement la valeur stockée dans le nœud de contenu JCR de la signature sous la propriété name
.
Pour ce faire, lœannotation de modèle Sling @ValueMapValue
est utilisée pour injecter la valeur dans un champ Java™ à lœaide de la ValueMap de la ressource de la requête.
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
public class BylineImpl implements Byline {
...
@ValueMapValue
private String name;
...
@Override
public String getName() {
return name;
}
...
}
Dans la mesure où la propriété JCR partage le nom en tant que champ Java™ (« nom » pour les deux), @ValueMapValue
résout automatiquement cette association et injecte la valeur de la propriété dans le champ Java™.
getOccupations() implementing-get-occupations
La méthode suivante à mettre en œuvre est getOccupations()
. Cette méthode charge les professions stockées dans la propriété JCR occupations
et renvoie une collection triée (par ordre alphabétique).
Avec la même technique explorée dans getName()
, la valeur de propriété peut être injectée dans le champ du modèle Sling.
Une fois que les valeurs de propriété JCR sont disponibles dans le modèle Sling via le champ Java™ injecté occupations
, la logique commerciale de tri peut être appliquée dans la méthode getOccupations()
.
import java.util.ArrayList;
import java.util.Collections;
...
public class BylineImpl implements Byline {
...
@ValueMapValue
private List<String> occupations;
...
@Override
public List<String> getOccupations() {
if (occupations != null) {
Collections.sort(occupations);
return new ArrayList<String>(occupations);
} else {
return Collections.emptyList();
}
}
...
}
...
isEmpty() implementing-is-empty
La dernière méthode publique est isEmpty()
, qui détermine à quel moment le composant doit se considérer comme « suffisamment créé » pour effectuer le rendu.
Pour ce composant, les exigences métier sont les trois champs suivants, name, image and occupations
qui doivent être renseignés avant que le composant soit rendu.
import org.apache.commons.lang3.StringUtils;
...
public class BylineImpl implements Byline {
...
@Override
public boolean isEmpty() {
if (StringUtils.isBlank(name)) {
// Name is missing, but required
return true;
} else if (occupations == null || occupations.isEmpty()) {
// At least one occupation is required
return true;
} else if (/* image is not null, logic to be determined */) {
// A valid image is required
return true;
} else {
// Everything is populated, so this component is not considered empty
return false;
}
}
...
}
Lutter contre le « problème d’image » tackling-the-image-problem
La vérification du nom et des conditions d’occupation est simple. Apache Commons Lang3 fournit la classe StringUtils pratique à utiliser. Cependant, il est difficile de savoir comment la présence de l’image peut être validée, car le composant Image des composants principaux est utilisé pour surfacer l’image.
Il existe deux façons de procéder :
Vérifiez si la propriété JCR fileReference
est résolue sur une ressource. OU convertissez cette ressource en modèle Sling d’image de composant principal et assurez-vous que la méthode getSrc()
n’est pas vide.
Utilisons la deuxième approche. La première approche est probablement suffisante, mais dans ce tutoriel, la deuxième est utilisée pour nous permettre d’explorer d’autres fonctionnalités des modèles Sling.
-
Créez une méthode privée qui récupère l’image. Cette méthode reste privée, car il n’est pas nécessaire d’exposer l’objet Image dans le HTL lui-même et elle est uniquement utilisée pour transmettre
isEmpty().
.Ajoutez la méthode privée suivante pour
getImage()
:code language-java import com.adobe.cq.wcm.core.components.models.Image; ... private Image getImage() { Image image = null; // Figure out how to populate the image variable! return image; }
Comme indiqué ci-dessus, il existe deux autres approches pour obtenir le Modèle Sling d’image :
La première utilise l’annotation
@Self
afin d’adapter automatiquement la requête active à l’Image.class
du composant principal.La seconde utilise le service OSGi Sling ModelFactory Apache. Pratique, il permet de créer des modèles Sling d’autres types dans le code Java™.
Utilisons la seconde approche.
note note NOTE Dans une implémentation réelle, la première approche, à savoir l’utilisation de @Self
, est préférable, car c’est la solution la plus simple et la plus élégante. Dans ce tutoriel, la deuxième approche est utilisée, car elle nécessite d’explorer plus de facettes des modèles Sling qui sont utiles pour des composants plus complexes.Étant donné que les modèles Sling sont des POJO Java™, et non des services OSGi, les annotations d’injection OSGi habituelles
@Reference
ne peuvent pas être utilisées. À leur place, les modèles Sling fournissent une annotation spéciale @OSGiService qui offre une fonctionnalité similaire. -
Mettez à jour
BylineImpl.java
pour inclure l’annotationOSGiService
pour injecter leModelFactory
:code language-java import org.apache.sling.models.factory.ModelFactory; import org.apache.sling.models.annotations.injectorspecific.OSGiService; ... public class BylineImpl implements Byline { ... @OSGiService private ModelFactory modelFactory; }
Avec le
ModelFactory
disponible, un modèle Sling d’image de composant principal peut être créé en utilisant :code language-java modelFactory.getModelFromWrappedRequest(SlingHttpServletRequest request, Resource resource, java.lang.Class<T> targetClass)
Toutefois, cette méthode nécessite à la fois une requête et une ressource qui ne sont pas encore disponibles dans le modèle Sling. Pour les obtenir, il faut utiliser davantage d’annotations du modèle Sling.
Pour obtenir la requête active, l’annotation @Self peut être utilisée pour injecter l’élément
adaptable
(qui est défini dans le@Model(..)
commeSlingHttpServletRequest.class
, dans un champ de classe Java™). -
Ajoutez l’annotation @Self pour obtenir la requête SlingHttpServletRequest :
code language-java import org.apache.sling.models.annotations.injectorspecific.Self; ... @Self private SlingHttpServletRequest request;
Rappelez-vous que l’utilisation de
@Self Image image
pour injecter le modèle Sling d’image de composant principal était une option ci-dessus. L’annotation@Self
essaie d’injecter l’objet adaptable (dans ce cas une SlingHttpServletRequest), et de s’adapter au type de champ de l’annotation. Puisque le modèle Sling d’image de composant principal est adaptable à partir des objets SlingHttpServletRequest, cela aurait fonctionné et représente moins de code que l’approchemodelFactory
plus exploratoire.Les variables nécessaires à l’instanciation du modèle d’image via l’API ModelFactory sont maintenant injectées. Utilisons l’annotation @PostConstruct du modèle Sling pour obtenir cet objet après l’instanciation du modèle Sling.
@PostConstruct
est incroyablement utile et agit dans une capacité similaire à celle d’un constructeur, cependant, il est invoqué après l’instanciation de la classe et l’injection de tous les champs Java™ annotés. Tandis que les autres annotations du modèle Sling annotent des champs de classe Java™ (variables),@PostConstruct
annote une méthode vide à zéro paramètre, typiquement nomméeinit()
(mais pouvant être nommée comme bon vous semble). -
Ajoutez la méthode @PostConstruct :
code language-java import javax.annotation.PostConstruct; ... public class BylineImpl implements Byline { ... private Image image; @PostConstruct private void init() { image = modelFactory.getModelFromWrappedRequest(request, request.getResource(), Image.class); } ... }
Rappelez-vous, les modèles Sling ne sont PAS des services OSGi, ce qui signifie que le maintien de l’état des classes est sûr. Souvent,
@PostConstruct
dérive et met en place l’état de la classe du modèle Sling pour une utilisation ultérieure, de manière similaire à ce que fait un constructeur ordinaire.Si la méthode
@PostConstruct
déclenche une exception, le modèle Sling n’est pas instancié et sa valeur est nulle. -
Vous pouvez désormais mettre à jour getImage() pour renvoyer simplement l’objet image.
code language-java /** * @return the Image Sling Model of this resource, or null if the resource cannot create a valid Image Sling Model. */ private Image getImage() { return image; }
-
Retournons à
isEmpty()
et terminons l’implémentation :code language-java @Override public boolean isEmpty() { final Image componentImage = getImage(); if (StringUtils.isBlank(name)) { // Name is missing, but required return true; } else if (occupations == null || occupations.isEmpty()) { // At least one occupation is required return true; } else if (componentImage == null || StringUtils.isBlank(componentImage.getSrc())) { // A valid image is required return true; } else { // Everything is populated, so this component is not considered empty return false; } }
Veuillez noter que les appels multiples à
getImage()
ne posent pas de problème, car ils renvoient la variable de classe initialiséeimage
et n’invoquent pasmodelFactory.getModelFromWrappedRequest(...)
, qui sans être trop coûteuse gagne tout de même à ne pas être appelée inutilement. -
La
BylineImpl.java
finale doit ressembler à :code language-java package com.adobe.aem.guides.wknd.core.models.impl; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.annotation.PostConstruct; import org.apache.commons.lang3.StringUtils; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.models.annotations.DefaultInjectionStrategy; import org.apache.sling.models.annotations.Model; import org.apache.sling.models.annotations.injectorspecific.OSGiService; import org.apache.sling.models.annotations.injectorspecific.Self; import org.apache.sling.models.annotations.injectorspecific.ValueMapValue; import org.apache.sling.models.factory.ModelFactory; import com.adobe.aem.guides.wknd.core.models.Byline; import com.adobe.cq.wcm.core.components.models.Image; @Model( adaptables = {SlingHttpServletRequest.class}, adapters = {Byline.class}, resourceType = {BylineImpl.RESOURCE_TYPE}, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL ) public class BylineImpl implements Byline { protected static final String RESOURCE_TYPE = "wknd/components/byline"; @Self private SlingHttpServletRequest request; @OSGiService private ModelFactory modelFactory; @ValueMapValue private String name; @ValueMapValue private List<String> occupations; private Image image; /** * @PostConstruct is immediately called after the class has been initialized * but BEFORE any of the other public methods. * It is a good method to initialize variables that is used by methods in the rest of the model * */ @PostConstruct private void init() { // set the image object image = modelFactory.getModelFromWrappedRequest(request, request.getResource(), Image.class); } @Override public String getName() { return name; } @Override public List<String> getOccupations() { if (occupations != null) { Collections.sort(occupations); return new ArrayList<String>(occupations); } else { return Collections.emptyList(); } } @Override public boolean isEmpty() { final Image componentImage = getImage(); if (StringUtils.isBlank(name)) { // Name is missing, but required return true; } else if (occupations == null || occupations.isEmpty()) { // At least one occupation is required return true; } else if (componentImage == null || StringUtils.isBlank(componentImage.getSrc())) { // A valid image is required return true; } else { // Everything is populated, so this component is not considered empty return false; } } /** * @return the Image Sling Model of this resource, or null if the resource cannot create a valid Image Sling Model. */ private Image getImage() { return image; } }
Signature HTL byline-htl
Dans le module ui.apps
, ouvrez /apps/wknd/components/byline/byline.html
qui a été créé dans la configuration précédente du composant AEM.
<div data-sly-use.placeholderTemplate="core/wcm/components/commons/v1/templates.html">
</div>
<sly data-sly-call="${placeholderTemplate.placeholder @ isEmpty=false}"></sly>
Passons en revue ce que fait ce script HTL jusqu’à présent :
-
Le
placeholderTemplate
pointe vers l’espace réservé des composants principaux, qui s’affiche lorsque le composant n’est pas entièrement configuré. Cela est rendu dans l’éditeur de page d’AEM Sites par une boîte avec le titre du composant, comme défini ci-dessus dans la propriétéjcr:title
ducq:Component
. -
Le
data-sly-call="${placeholderTemplate.placeholder @ isEmpty=false}
charge leplaceholderTemplate
défini ci-dessus et passe une valeur booléenne (actuellement codée de manière irréversible surfalse
) dans le modèle de l’espace réservé. LorsqueisEmpty
est true, le modèle d’espace réservé rend la boîte grise, sinon il ne rend rien.
Mettre à jour la signature HTL
-
Mettez à jour byline.html avec la structure HTML squelettique suivante :
code language-html <div data-sly-use.placeholderTemplate="core/wcm/components/commons/v1/templates.html" class="cmp-byline"> <div class="cmp-byline__image"> <!--/* Include the Core Components Image Component */--> </div> <h2 class="cmp-byline__name"><!--/* Include the name */--></h2> <p class="cmp-byline__occupations"><!--/* Include the occupations */--></p> </div> <sly data-sly-call="${placeholderTemplate.placeholder @ isEmpty=true}"></sly>
Veuillez noter que les classes CSS suivent la convention de nommage BEM. Bien que l’utilisation des conventions BEM ne soit pas obligatoire, elle est recommandée, car elle est utilisée dans les classes CSS des composants principaux et aboutit généralement à des règles CSS propres et lisibles.
Instancier des objets de modèle Sling en HTL instantiating-sling-model-objects-in-htl
L’instruction Use block est utilisée pour instancier les objets du modèle Sling dans le script HTL et l’affecter à une variable HTL.
Le data-sly-use.byline="com.adobe.aem.guides.wknd.models.Byline"
utilise l’interface de signature (com.adobe.aem.guides.wknd.models.Byline) implémentée par BylineImpl et y adapte la SlingHttpServletRequest en cours, et le résultat est stocké dans une signature de nom variable HTL (data-sly-use.<variable-name>
).
-
Mettez à jour le
div
extérieur pour référencer le modèle Sling de signature par son interface publique :code language-xml <div data-sly-use.byline="com.adobe.aem.guides.wknd.core.models.Byline" data-sly-use.placeholderTemplate="core/wcm/components/commons/v1/templates.html" class="cmp-byline"> ... </div>
Accès aux méthodes de modèle Sling accessing-sling-model-methods
HTL emprunte à JSTL, et utilise la même façon de raccourcir les noms des méthodes getter Java™.
Par exemple, invoquer la méthode getName()
du modèle Sling de signature peut être raccourci en byline.name
, de même byline.isEmpty
peut être raccourci en byline.empty
. L’utilisation des noms complets des méthodes, byline.getName
ou byline.isEmpty
, fonctionne également. Veuillez noter que les ()
ne sont jamais utilisés pour invoquer des méthodes en HTL (similaire au JSTL).
Les méthodes Java™ qui nécessitent un paramètre ne peuvent pas être utilisées en HTL. Il en est ainsi pour que la logique du HTL reste simple.
-
Le nom Signature peut être ajouté au composant en invoquant la méthode
getName()
sur le modèle Sling de signature, ou en HTL :${byline.name}
.Mettez à jour la balise
h2
:code language-xml <h2 class="cmp-byline__name">${byline.name}</h2>
Utilisation des options d’expression HTL using-htl-expression-options
Les options d'expressions HTL agissent comme des modificateurs sur le contenu en HTL, et vont du formatage de la date à la traduction i18n. Les expressions peuvent également être utilisées pour joindre des listes ou des tableaux de valeurs, ce qui est nécessaire pour afficher les professions dans un format délimité par des virgules.
Les expressions sont ajoutées via l’opérateur @
dans l’expression HTL.
-
Pour joindre la liste des professions avec ", ", le code suivant est utilisé :
code language-html <p class="cmp-byline__occupations">${byline.occupations @ join=', '}</p>
Affichage conditionnel de l’espace réservé conditionally-displaying-the-placeholder
La plupart des scripts HTL pour les composants AEM utilisent le paradigme d’espace réservé pour fournir un indice visuel aux auteurs indiquant qu'un composant est incorrectement authentifié et qu’il ne s’affiche pas sur AEM Publish. La convention pour guider cette décision est d’implémenter une méthode sur le modèle Sling de soutien du composant, dans ce cas : Byline.isEmpty()
.
La méthode isEmpty()
est invoquée sur le modèle Sling de signature et le résultat (ou plutôt son négatif, via l’opérateur !
) est enregistré dans une variable HTL nommée hasContent
:
-
Mettez à jour le code
div
extérieur pour sauvegarder une variable HTL nomméehasContent
:code language-html <div data-sly-use.byline="com.adobe.aem.guides.wknd.core.models.Byline" data-sly-use.placeholderTemplate="core/wcm/components/commons/v1/templates.html" data-sly-test.hasContent="${!byline.empty}" class="cmp-byline"> ... </div>
Veuillez noter l’utilisation de
data-sly-test
, le bloc HTLtest
est essentiel, il définit une variable HTL et rend/ne rend pas l’élément HTML sur lequel il se trouve. Il est basé sur le résultat de l’évaluation de l’expression HTL. Si la valeur est "true", l’élément HTML est rendu, sinon il ne l’est pas.Cette variable HTL
hasContent
peut maintenant être réutilisée pour afficher/masquer l’espace réservé de manière conditionnelle. -
Mettez à jour l’appel conditionnel au
placeholderTemplate
au bas du fichier avec les éléments suivants :code language-html <sly data-sly-call="${placeholderTemplate.placeholder @ isEmpty=!hasContent}"></sly>
Afficher l’image à l’aide des composants principaux using-the-core-components-image
Le script HTL pour byline.html
est maintenant presque complet et il ne manque que l’image.
Comme le sling:resourceSuperType
pointe vers le composant Image des composants principaux pour créer l'image, le composant Image des composants principaux peut être utilisé pour rendre l’image.
Pour cela, incluons la ressource de signature actuelle, mais forçons le type de ressource du composant Image des composants principaux, en utilisant le type de ressource core/wcm/components/image/v2/image
. Il s’agit d’un modèle puissant pour la réutilisation des composants. Pour cela, le bloc data-sly-resource
de HTL est utilisé.
-
Remplacez le
div
par une classe decmp-byline__image
avec ce qui suit :code language-html <div class="cmp-byline__image" data-sly-resource="${ '.' @ resourceType = 'core/wcm/components/image/v2/image' }"></div>
Ce
data-sly-resource
, inclut la ressource courante via le chemin relatif'.'
, et force l’inclusion de la ressource courante (ou de la ressource de contenu de la signature) avec le type de ressource decore/wcm/components/image/v2/image
.Le type de ressource Composants principaux est utilisé directement, et non par l’intermédiaire d’un proxy, car il s’agit d’une utilisation in-script et elle n’est jamais conservée dans le contenu.
-
byline.html
complété ci-dessous :code language-html <!--/* byline.html */--> <div data-sly-use.byline="com.adobe.aem.guides.wknd.core.models.Byline" data-sly-use.placeholderTemplate="core/wcm/components/commons/v1/templates.html" data-sly-test.hasContent="${!byline.empty}" class="cmp-byline"> <div class="cmp-byline__image" data-sly-resource="${ '.' @ resourceType = 'core/wcm/components/image/v2/image' }"> </div> <h2 class="cmp-byline__name">${byline.name}</h2> <p class="cmp-byline__occupations">${byline.occupations @ join=', '}</p> </div> <sly data-sly-call="${placeholderTemplate.placeholder @ isEmpty=!hasContent}"></sly>
-
Déployez la base de code sur une instance locale d’AEM. Étant donné que des modifications ont été apportées à
core
etui.apps
, les deux modules doivent être déployés.code language-shell $ cd aem-guides-wknd/ui.apps $ mvn clean install -PautoInstallPackage
code language-shell $ cd ../core $ mvn clean install -PautoInstallBundle
Pour déployer vers AEM 6.5/6.4, invoquez le profil
classic
:code language-shell $ cd ../core $ mvn clean install -PautoInstallBundle -Pclassic
note caution CAUTION Vous pourriez également compiler le projet entier à partir de la racine en utilisant le profil Maven autoInstallSinglePackage
mais cela pourrait écraser les changements de contenu sur la page. Cela est dû au fait que leui.content/src/main/content/META-INF/vault/filter.xml
a été modifié pour le code de démarrage du tutoriel afin de remplacer proprement le contenu AEM existant. Dans un scénario en situation réelle, ce n’est pas un problème.
Révision du composant de signature non stylisé reviewing-the-unstyled-byline-component
-
Après avoir déployé la mise à jour, accédez à la page Ultimate Guide to LA Skateparks, ou à l’endroit où vous avez ajouté le composant de signature plus tôt dans le chapitre.
-
Les image, nom, et professions apparaissent désormais et un composant de signature non stylisé, mais fonctionnel, est présent.
Révision de l’enregistrement du modèle Sling reviewing-the-sling-model-registration
La vue Statut des modèles Sling de la console Web d’AEM affiche tous les modèles Sling enregistrés dans AEM. Le modèle Sling de signature peut être validé comme étant installé et reconnu en examinant cette liste.
Si le BylineImpl ne s’affiche pas dans cette liste, il s’agit probablement d’un problème avec les annotations du modèle Sling ou bien le modèle n’a pas été ajouté au bon package (com.adobe.aem.guides.wknd.core.models
) dans le projet de base.
http://localhost:4502/system/console/status-slingmodels
Styles de signature byline-styles
Pour aligner le composant de signature sur le design créatif fourni, nous lui donnons un style. Ceci est réalisé en utilisant un fichier SCSS et en mettant à jour le fichier dans le module ui.frontend.
Ajouter un style par défaut
Ajoutez des styles par défaut pour le composant de signature.
-
Revenez à l’IDE et au projet ui.frontend sous
/src/main/webpack/components
: -
Créez un fichier nommé
_byline.scss
. -
Ajoutez le CSS des implémentations de signature (écrit en SCSS) dans le
_byline.scss
:code language-scss .cmp-byline { $imageSize: 60px; .cmp-byline__image { float: left; /* This class targets a Core Component Image CSS class */ .cmp-image__image { width: $imageSize; height: $imageSize; border-radius: $imageSize / 2; object-fit: cover; } } .cmp-byline__name { font-size: $font-size-medium; font-family: $font-family-serif; padding-top: 0.5rem; margin-left: $imageSize + 25px; margin-bottom: .25rem; margin-top:0rem; } .cmp-byline__occupations { margin-left: $imageSize + 25px; color: $gray; font-size: $font-size-xsmall; text-transform: uppercase; } }
-
Ouvrez un terminal et accédez au module
ui.frontend
. -
Démarrez le processus
watch
avec la commande npm suivante :code language-shell $ cd ui.frontend/ $ npm run watch
-
Retournez au navigateur et accédez à l’article LA SkateParks. Vous devriez voir les styles mis à jour pour le composant.
note tip TIP Vous pouvez vider la mémoire cache du navigateur pour vous assurer que le fichier CSS obsolète n’est pas diffusé et actualiser la page avec le composant de signature pour obtenir le style complet.
Félicitations. congratulations
Félicitations, vous avez créé entièrement un composant personnalisé à l’aide d’Adobe Experience Manager.
Étapes suivantes next-steps
Continuez votre apprentissage du développement des composants d’AEM en explorant la manière d’écrire des tests JUnit pour le code Java™ de signature. Vous aurez ainsi l’assurance que tout est développé correctement et que la logique commerciale implémentée est correcte et complète.
Affichez le code terminé sur GitHub ou révisez et déployez le code localement sur la branche Git tutorial/custom-component-solution
.
- Clonez le référentiel github.com/adobe/aem-guides-wknd.
- Consultez la branche
tutorial/custom-component-solution