Egen komponent custom-component

I den här självstudiekursen beskrivs hur du skapar en anpassad Byline AEM-komponent som visar innehåll som har skapats i en dialogruta och utforskar hur du utvecklar en delningsmodell för att kapsla in affärslogik som fyller i komponentens HTML-kod.

Förutsättningar prerequisites

Granska de verktyg och instruktioner som krävs för att konfigurera en lokal utvecklingsmiljö.

Startprojekt

NOTE
Om du har slutfört det föregående kapitlet kan du återanvända projektet och hoppa över stegen för att checka ut startprojektet.

Ta en titt på den baslinjekod som självstudiekursen bygger på:

  1. Kolla in grenen tutorial/custom-component-start från GitHub

    code language-shell
    $ cd aem-guides-wknd
    $ git checkout tutorial/custom-component-start
    
  2. Distribuera kodbasen till en lokal AEM med dina Maven-kunskaper:

    code language-shell
    $ mvn clean install -PautoInstallSinglePackage
    
    note note
    NOTE
    Om du använder AEM 6.5 eller 6.4 lägger du till profilen classic till eventuella Maven-kommandon.
    code language-shell
    $ mvn clean install -PautoInstallSinglePackage -Pclassic
    

Du kan alltid visa den färdiga koden på GitHub eller checka ut koden lokalt genom att växla till grenen tutorial/custom-component-solution.

Syfte

  1. Förstå hur du skapar en anpassad AEM
  2. Lär dig kapsla in affärslogik med Sling Models
  3. Förstå hur du använder en segmenteringsmodell i ett HTML-skript

Vad du ska bygga what-build

I den här delen av WKND-självstudien skapas en Byline-komponent som används för att visa redigerad information om en artikels medverkande.

exempel på instickskomponent

Byline-komponent

Implementeringen av komponenten Byline innehåller en dialogruta som samlar in innehållet och en anpassad Sling-modell som hämtar information som:

  • Namn
  • Bild
  • Yrken

Skapa Byline-komponent create-byline-component

Skapa först nodstrukturen för Byline-komponenten och definiera en dialogruta. Detta representerar komponenten i AEM och definierar implicit komponentens resurstyp genom sin placering i JCR-läsaren.

Dialogrutan visar gränssnittet som innehållsförfattare kan använda. För den här implementeringen används den AEM WCM Core-komponentens Image -komponent för att hantera redigering och återgivning av Bylines bild, så den måste anges som den här komponentens sling:resourceSuperType.

Skapa komponentdefinition create-component-definition

  1. Navigera till /apps/wknd/components i modulen ui.apps och skapa en mapp med namnet byline.

  2. Lägg till en fil med namnet .content.xml i mappen byline

    dialogruta för att skapa nod

  3. Fyll i filen .content.xml med följande:

    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"/>
    

    Ovanstående XML-fil innehåller definitionen för komponenten, inklusive rubrik, beskrivning och grupp. sling:resourceSuperType pekar på core/wcm/components/image/v2/image, som är kärnbildkomponenten.

Skapa HTML-skriptet create-the-htl-script

  1. I mappen byline lägger du till filen byline.html som ansvarar för komponentens HTML-presentation. Det är viktigt att ge filen samma namn som mappen eftersom det blir standardskriptet som Sling använder för att återge den här resurstypen.

  2. Lägg till följande kod i 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>
    

byline.html revideras senare när delningsmodellen har skapats. HTML-filens aktuella läge gör att komponenten kan visas i ett tomt läge i AEM Sites sidredigerare när den dras och släpps på sidan.

Skapa dialogdefinitionen create-the-dialog-definition

Definiera sedan en dialogruta för den inbyggda komponenten med följande fält:

  • Namn: ett textfält som medarbetarens namn tillhör.
  • Bild: en referens till medarbetarens biobild.
  • Ytor: en lista över yrken som har tilldelats medarbetaren. Ytor ska sorteras alfabetiskt i stigande ordning (a till z).
  1. I mappen byline skapar du en mapp med namnet _cq_dialog.

  2. I byline/_cq_dialog lägger du till en fil med namnet .content.xml. Det här är XML-definitionen för dialogrutan. Lägg till följande XML:

    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>
    

    De här noddefinitionerna i dialogrutan använder Dela resurssammanfogning för att kontrollera vilka dialogrutor som ärvs från komponenten sling:resourceSuperType, i det här fallet kärnkomponenterna' Image component .

    slutförd dialogruta för byline

Skapa dialogrutan Princip create-the-policy-dialog

På samma sätt som när du skapar en dialogruta skapar du en principdialogruta (tidigare kallad designdialogruta) som döljer oönskade fält i principkonfigurationen som ärvts från kärnkomponentens image-komponent.

  1. I mappen byline skapar du en mapp med namnet _cq_design_dialog.

  2. I byline/_cq_design_dialog skapar du en fil med namnet .content.xml. Uppdatera filen med följande: med följande XML. Det är enklast att öppna .content.xml och kopiera/klistra in XML-filen nedan i den.

    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>
    

    Basen för föregående principdialogruta XML hämtades från kärnkomponentavbildningskomponenten.

    Precis som i dialogrutan används Dela resurssammanslagning för att dölja irrelevanta fält som annars ärvs från sling:resourceSuperType, vilket visas i noddefinitionerna med egenskapen sling:hideResource="{Boolean}true".

Distribuera koden deploy-the-code

  1. Synkronisera ändringarna i ui.apps med din IDE eller med dina Maven-kunskaper.

    Exportera till AEM serverns originalkomponent

Lägga till komponenten på en sida add-the-component-to-a-page

Om du vill hålla saker enkla och fokusera på AEM komponentutveckling kan vi lägga till den inbyggda komponenten i det aktuella läget på en artikelsida för att kontrollera att noddefinitionen cq:Component är korrekt. Verifiera också att AEM känner igen den nya komponentdefinitionen och att komponentens dialogruta fungerar för redigering.

Lägga till en bild i AEM Assets

Ladda först upp ett provhuvud som tagits till AEM Assets för att fylla i bilden i Byline-komponenten.

  1. Navigera till mappen LA Skateparks i AEM Assets: http://localhost:4502/assets.html/content/dam/wknd/en/magazine/la-skateparks.

  2. Överför huvudbilden för stacey-roswells.jpg till mappen.

    Headshot uploaded to AEM Assets

Skapa komponenten author-the-component

Lägg sedan till komponenten Byline på en sida i AEM. Eftersom komponenten Byline läggs till i komponentgruppen WKND-platser - innehåll, via definitionen ui.apps/src/main/content/jcr_root/apps/wknd/components/byline/.content.xml, är den automatiskt tillgänglig för alla behållare vars princip tillåter komponentgruppen WKND-platser - innehåll . Det är alltså tillgängligt i artikelsidans layoutbehållare.

  1. Gå till LA Skatepark-artikeln på: http://localhost:4502/editor.html/content/wknd/us/en/magazine/guide-la-skateparks.html

  2. Dra och släpp en Byline-komponent från vänster sidofält till nederst i layoutbehållaren för den öppnade artikelsidan.

    lägg till en instickskomponent på sidan

  3. Kontrollera att vänster sidofält är öppet och synligt och att Resurssökaren** är markerat.

  4. Välj platshållaren för instickskomponenten, som i sin tur visar åtgärdsfältet och trycker på ikonen skiftnyckel för att öppna dialogrutan.

  5. Öppna dialogrutan och den första fliken (Tillgång) aktiv, öppna det vänstra sidofältet och dra en bild till bildens släppzon från resurssökaren. Sök efter"stacey" för att hitta biobilden Stacey Roswells som finns i WKND-paketet ui.content.

    lägg till bild i dialogrutan

  6. När du har lagt till en bild klickar du på fliken Egenskaper för att ange Namn och Yrken.

    När du anger befattningar anger du dem i omvänd alfabetisk ordning så att den alfabetiska affärslogik som implementeras i försäljningsmodellen verifieras.

    Tryck på knappen Klar längst ned till höger för att spara ändringarna.

    fyll i egenskaperna för den inbyggda komponenten

    AEM konfigurerar och redigerar komponenter via dialogrutorna. I nuläget ingår dialogrutorna för datainsamling i utvecklingen av komponenten Byline, men logiken för att återge det redigerade innehållet har ännu inte lagts till. Därför visas bara platshållaren.

  7. När du har sparat dialogrutan går du till CRXDE Lite och granskar hur komponentens innehåll lagras på innehållsnoden för instickskomponenten under AEM.

    Hitta innehållsnoden för komponenten Byline under sidan LA Skate Parks, dvs. /content/wknd/us/en/magazine/guide-la-skateparks/jcr:content/root/container/container/byline.

    Observera att egenskapsnamnen name, occupations och fileReference lagras på byline-noden.

    Observera också att noden sling:resourceType är inställd på wknd/components/content/byline vilket är det som binder den här innehållsnoden till implementeringen av komponenten Byline.

    byline-egenskaper i CRXDE

Skapa Byline Sling Model create-sling-model

Sedan skapar vi en Sling-modell som fungerar som datamodell och lagrar affärslogiken för Byline-komponenten.

Sling Models är anteckningsdrivna Java™ POJOs (Plain Old Java™ Objects) som gör det enklare att mappa data från JCR till Java™-variabler och som ger ökad effektivitet vid utveckling i AEM.

Granska Maven Dependencies maven-dependency

Byline Sling Model bygger på flera Java™-API:er från AEM. Dessa API:er är tillgängliga via dependencies som anges i POM-filen för modulen core. Det projekt som används för den här självstudiekursen har skapats för AEM as a Cloud Service. Den är dock unik eftersom den är bakåtkompatibel med AEM 6.5/6.4. Därför ingår både beroenden för Cloud Service och AEM 6.x.

  1. Öppna filen pom.xml under <src>/aem-guides-wknd/core/pom.xml.

  2. Hitta beroendet för aem-sdk-api - endast AEM as a Cloud Service

    code language-xml
    <dependency>
        <groupId>com.adobe.aem</groupId>
        <artifactId>aem-sdk-api</artifactId>
    </dependency>
    

    aem-sdk-api innehåller alla publika Java™-API:er som exponeras av AEM. aem-sdk-api används som standard när det här projektet skapas. Versionen finns kvar i den överordnade reaktorversionen från projektets rot vid aem-guides-wknd/pom.xml.

  3. Hitta beroendet för uber-jar - AEM endast 6.5/6.4

    code language-xml
    ...
        <dependency>
            <groupId>com.adobe.aem</groupId>
            <artifactId>uber-jar</artifactId>
            <classifier>apis</classifier>
        </dependency>
    ...
    

    uber-jar inkluderas bara när profilen classic anropas, dvs. mvn clean install -PautoInstallSinglePackage -Pclassic. Detta är unikt för det här projektet. I ett verkligt projekt som genereras från den AEM projektarkitypen är uber-jar standardvärdet om den AEM versionen är 6.5 eller 6.4.

    uber-jar innehåller alla publika Java™-API:er som exponeras av AEM 6.x. Versionen bibehålls i den överordnade reaktorversionen från projektets rot aem-guides-wknd/pom.xml .

  4. Hitta beroendet för core.wcm.components.core:

    code language-xml
     <!-- Core Component Dependency -->
        <dependency>
            <groupId>com.adobe.cq</groupId>
            <artifactId>core.wcm.components.core</artifactId>
        </dependency>
    

    Detta är de fullständiga Java™-API:erna som exponeras av AEM Core Components. AEM kärnkomponenter är ett projekt som underhålls utanför AEM och därför har en separat releasecykel. Därför är det ett beroende som måste inkluderas separat och ingår inte i uber-jar eller aem-sdk-api.

    Precis som för uber-jar bevaras versionen för det här beroendet i den överordnade reaktorns PDF-fil från aem-guides-wknd/pom.xml.

    Senare i den här självstudiekursen används klassen Core Component Image för att visa bilden i komponenten Byline. Det är nödvändigt att ha beroendet av kärnkomponenten för att kunna skapa och kompilera Sling-modellen.

Byline-gränssnitt byline-interface

Skapa ett publikt Java™-gränssnitt för Byline. Byline.java definierar de publika metoder som krävs för att köra HTML-skriptet byline.html.

  1. I core-modulen i mappen core/src/main/java/com/adobe/aem/guides/wknd/core/models skapas en fil med namnet Byline.java

    skapa gränssnitt

  2. Uppdatera Byline.java med följande metoder:

    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();
    }
    

    De första två metoderna visar värdena för name och ockupationer för Byline-komponenten.

    Metoden isEmpty() används för att avgöra om komponenten har något innehåll att återge eller om den väntar på att konfigureras.

    Observera att det inte finns någon metod för bilden. Den granskas senare.

  3. Java™-paket som innehåller offentliga Java™-klasser, i det här fallet en Sling-modell, måste versionshanteras med paketets package-info.java-fil.

    Eftersom WKND-källans Java™-paket com.adobe.aem.guides.wknd.core.models deklarerar version av 1.0.0 och ett hårt offentligt gränssnitt och metoder läggs till, måste versionen ökas till 1.1.0. Öppna filen på core/src/main/java/com/adobe/aem/guides/wknd/core/models/package-info.java och uppdatera @Version("1.0.0") till @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;
    

När du ändrar filerna i det här paketet måste paketversionen justeras semantiskt. Annars identifieras en ogiltig paketversion av Maven-projektets bnd-baseline-maven-plugin och den inbyggda versionen bryts. Som tur är rapporterar Maven-pluginen den ogiltiga Java™-paketversionen och den version den ska vara. Uppdatera deklarationen @Version("...") i det felaktiga Java™-paketets package-info.java till den version som rekommenderas av plugin-programmet för att korrigera.

Byline implementation byline-implementation

BylineImpl.java är implementeringen av Sling-modellen som implementerar gränssnittet Byline.java som definierats tidigare. Den fullständiga koden för BylineImpl.java finns längst ned i det här avsnittet.

  1. Skapa en mapp med namnet impl under core/src/main/java/com/adobe/aem/guides/core/models.

  2. Skapa en fil BylineImpl.java i mappen impl.

    Byline Impl-fil

  3. Öppna BylineImpl.java. Ange att gränssnittet Byline implementeras. Använd de automatiska funktionerna för IDE eller uppdatera filen manuellt för att inkludera de metoder som krävs för att implementera gränssnittet Byline:

    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;
        }
    }
    
  4. Lägg till Sling Model-anteckningar genom att uppdatera BylineImpl.java med följande anteckningar på klassnivå. Den här @Model(..)anteckningen är det som gör klassen till en Sling-modell.

    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";
        ...
    }
    

    Låt oss granska den här kommentaren och dess parametrar:

    • @Model-anteckningen registrerar BylineImpl som en Sling-modell när den distribueras till AEM.
    • Parametern adaptables anger att modellen kan anpassas av begäran.
    • Parametern adapters tillåter att implementeringsklassen registreras under Byline-gränssnittet. Detta gör att HTML-skriptet kan anropa Sling-modellen via gränssnittet (i stället för direkt implementering). Mer information om adaptrar finns här.
    • resourceType pekar på typen av Byline-komponentresurs (som skapades tidigare) och hjälper till att lösa rätt modell om det finns flera implementeringar. Mer information om hur du associerar en modellklass med en resurstyp finns här.

Implementera Sling Model-metoder implementing-the-sling-model-methods

getName() implementing-get-name

Den första metoden som implementeras är getName(), men returnerar helt enkelt det värde som lagras till bylines JCR-innehållsnod under egenskapen name.

För detta används @ValueMapValue Sling Model-anteckningen för att mata in värdet i ett Java™-fält med hjälp av Request-resursens ValueMap.

import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;

public class BylineImpl implements Byline {
    ...
    @ValueMapValue
    private String name;

    ...
    @Override
    public String getName() {
        return name;
    }
    ...
}

Eftersom JCR-egenskapen delar namn som Java™-fält (båda är "namn"), löser @ValueMapValue automatiskt associationen och injicerar egenskapens värde i Java™-fältet.

getOccupations() implementing-get-occupations

Nästa metod som ska implementeras är getOccupations(). Den här metoden läser in de befattningar som lagras i JCR-egenskapen occupations och returnerar en sorterad (i bokstavsordning) samling av dem.

Om du använder samma teknik som beskrivs i getName() kan egenskapsvärdet injiceras i fältet för delningsmodellen.

När JCR-egenskapsvärdena är tillgängliga i Sling Model via det inmatade Java™-fältet occupations kan sorteringsaffärslogiken användas i metoden 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

Den sista publika metoden är isEmpty(), som avgör när komponenten ska se sig själv som"tillräckligt skapad" för att kunna återge.

För den här komponenten är affärskravet alla tre fält, name, image and occupations måste fyllas i innan komponenten kan återges.

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;
        }
    }
    ...
}

Hantering av "Bildproblem" tackling-the-image-problem

Det är enkelt att kontrollera namn och villkor för yrket och Apache Commons Lang3 innehåller den praktiska klassen StringUtils. Det är dock oklart hur närvaron av bilden kan valideras eftersom kärnkomponentavbildningskomponenten används för att visa bilden.

Det finns två sätt att ta itu med detta:

Kontrollera om JCR-egenskapen fileReference matchar en resurs. OR Konvertera den här resursen till en Core Component Image Sling Model och kontrollera att metoden getSrc() inte är tom.

Vi använder metoden second. Det första tillvägagångssättet är förmodligen tillräckligt, men i den här självstudien används det senare för att vi ska kunna utforska andra funktioner i Sling Models.

  1. Skapa en privat metod som hämtar bilden. Den här metoden är privat eftersom det inte finns något behov av att visa bildobjektet i själva HTML-koden, och den används bara för att köra isEmpty().

    Lägg till följande privata metod för 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;
    }
    

    Som vi nämnt ovan finns det ytterligare två sätt att hämta Image Sling Model:

    Den första använder anteckningen @Self för att automatiskt anpassa den aktuella begäran till kärnkomponentens Image.class

    Den andra använder Apache Sling ModelFactory OSGi-tjänsten, som är en praktisk tjänst, och hjälper oss att skapa Sling-modeller av andra typer i Java™-kod.

    Låt oss använda den andra metoden.

    note note
    NOTE
    I en implementering i verkligheten är det bäst att använda metoden One, med @Self eftersom det är den enklare och mer eleganta lösningen. I den här självstudiekursen används den andra metoden eftersom den kräver att du utforskar fler aspekter av Sling Models som är användbara är mer komplexa komponenter!

    Eftersom Sling Models är Java™ POJO och inte OSGi Services, kan de vanliga OSGi-injektionskommentarerna @Reference inte användas, i stället tillhandahåller Sling Models en speciell @OSGiService-anteckning som ger liknande funktioner.

  2. Uppdatera BylineImpl.java så att den innehåller anteckningen OSGiService för att mata in ModelFactory:

    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;
    }
    

    När ModelFactory är tillgängligt kan en Core Component Image Sling-modell skapas med:

    code language-java
    modelFactory.getModelFromWrappedRequest(SlingHttpServletRequest request, Resource resource, java.lang.Class<T> targetClass)
    

    Den här metoden kräver dock både en begäran och en resurs, som ännu inte är tillgänglig i Sling-modellen. Fler Sling Model-anteckningar används för att få dessa.

    För att hämta den aktuella begäran kan anteckningen @Self användas för att mata in adaptable (som definieras i @Model(..) som SlingHttpServletRequest.class) i ett Java™-klassfält.

  3. Lägg till anteckningen @Self för att hämta begäran SlingHttpServletRequest:

    code language-java
    import org.apache.sling.models.annotations.injectorspecific.Self;
    ...
    @Self
    private SlingHttpServletRequest request;
    

    Kom ihåg att när du använder @Self Image image för att mata in Core Component Image Sling Model var ett alternativ ovan - @Self-anteckningen försöker mata in det adapterbara objektet (i det här fallet en SlingHttpServletRequest) och anpassa sig till anteckningsfältstypen. Eftersom Core Component Image Sling Model kan anpassas från SlingHttpServletRequest-objekt, skulle detta ha fungerat och är mindre kod än mer utforskande modelFactory-metod.

    Nu injiceras de variabler som krävs för att instansiera Image-modellen via API:t ModelFactory. Låt oss använda Sling Model-anteckningen @PostConstruct för att hämta det här objektet efter att Sling Model har instansierats.

    @PostConstruct är mycket användbart och fungerar i liknande kapacitet som en konstruktor, men anropas när klassen har instansierats och alla kommenterade Java™-fält har injicerats. Medan andra Sling Model-anteckningar kommenterar Java™-klassfält (variabler), kommenterar @PostConstruct en void, noll-parametermetod, vanligtvis med namnet init() (men kan namnges vad som helst).

  4. Lägg till metoden @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);
        }
        ...
    }
    

    Kom ihåg att Sling Models är NOT OSGi Services, så det är säkert att behålla klasstillstånd. @PostConstruct hämtar och ställer ofta in Sling Model-klasstillstånd för senare användning, som liknar det som en vanlig konstruktor gör.

    Om metoden @PostConstruct genererar ett undantag skapas ingen instans av Sling-modellen och den är null.

  5. getImage() kan nu uppdateras för att returnera bildobjektet.

    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;
    }
    
  6. Vi går tillbaka till isEmpty() och slutför implementeringen:

    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;
        }
    }
    

    Observera att flera anrop till getImage() inte är problematiska eftersom returnerar den initierade image class-variabeln och inte anropar modelFactory.getModelFromWrappedRequest(...) som inte är alltför dyr, men som bör undvikas att anropa i onödan.

  7. Den sista BylineImpl.java ska se ut så här:

    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;
        }
    }
    

Byline HTML byline-htl

I modulen ui.apps öppnar du /apps/wknd/components/byline/byline.html som skapades i den tidigare konfigurationen av AEM.

<div data-sly-use.placeholderTemplate="core/wcm/components/commons/v1/templates.html">
</div>
<sly data-sly-call="${placeholderTemplate.placeholder @ isEmpty=false}"></sly>

Låt oss se vad detta HTML-skript gör hittills:

  • placeholderTemplate pekar på Core Components platshållare, som visas när komponenten inte är helt konfigurerad. Det här återger i AEM Sites Page Editor som en ruta med komponenttiteln, enligt definitionen ovan i jcr:title-egenskapen för cq:Component.

  • data-sly-call="${placeholderTemplate.placeholder @ isEmpty=false} läser in placeholderTemplate som definierats ovan och skickar ett booleskt värde (som för närvarande är hårdkodat till false) till platshållarmallen. När isEmpty är true återges den grå rutan av platshållarmallen, annars återges ingenting.

Uppdatera textmarkör-HTML

  1. Uppdatera byline.html med följande HTML-struktur för skelett:

    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>
    

    Observera att CSS-klasserna följer BEM-namnkonventionen. BEM-konventioner är inte obligatoriska, men BEM rekommenderas eftersom det används i CSS-klasser för kärnkomponenter och i allmänhet leder till rena, läsbara CSS-regler.

Instansierar Sling Model-objekt i HTML instantiating-sling-model-objects-in-htl

Använd blockprogramsats används för att instansiera Sling Model-objekt i HTL-skriptet och tilldela den till en HTL-variabel.

data-sly-use.byline="com.adobe.aem.guides.wknd.models.Byline" använder det Byline-gränssnitt (com.adobe.aem.guides.wknd.models.Byline) som implementeras av BylineImpl och anpassar den aktuella SlingHttpServletRequest till det, och resultatet lagras i en HTML-variabelnamnsbit ( data-sly-use.<variable-name>).

  1. Uppdatera den yttre div så att den refererar till Byline Sling Model via dess publika gränssnitt:

    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>
    

Åtkomst till Sling Model-metoder accessing-sling-model-methods

HTML lånar från JSTL och använder samma förkortning av Java™-get-metodnamn.

Anrop av getName()-metoden för insticksmodellen kan till exempel förkortas till byline.name, i stället för till byline.isEmpty kan det förkortas till byline.empty. Fullständiga metodnamn, byline.getName eller byline.isEmpty, fungerar också. Observera att () aldrig används för att anropa metoder i HTML (liknande JSTL).

Java™-metoder som kräver parametern kan inte användas i HTML. Detta är utformat för att göra logiken i HTML enkel.

  1. Du kan lägga till namnet Byline i komponenten genom att anropa metoden getName() i modellen Byline Sling eller i HTML: ${byline.name}.

    Uppdatera taggen h2:

    code language-xml
    <h2 class="cmp-byline__name">${byline.name}</h2>
    

Använda alternativ för HTML-uttryck using-htl-expression-options

Alternativ för HTML-uttryck fungerar som modifierare för innehållet i HTML och varierar från datumformatering till i18n-översättning. Uttryck kan också användas för att sammanfoga listor eller värdematriser, vilket är vad som behövs för att visa arbetsuppgifterna i ett kommaavgränsat format.

Uttryck läggs till via operatorn @ i HTL-uttrycket.

  1. Om du vill gå med i listan över yrken med ", " används följande kod:

    code language-html
    <p class="cmp-byline__occupations">${byline.occupations @ join=', '}</p>
    

Villkorlig visning av platshållaren conditionally-displaying-the-placeholder

De flesta HTML-skript för AEM Components använder platshållarparadigm för att ge en visuell referens till författare som anger att en komponent är felaktigt skapad och inte visas på AEM Publish. Konventionen som styr det här beslutet är att implementera en metod på komponentens bakomliggande Sling Model, i det här fallet: Byline.isEmpty().

Metoden isEmpty() anropas i Byline Sling Model och resultatet (eller snarare negativt, via operatorn !) sparas i en HTML-variabel med namnet hasContent:

  1. Uppdatera den yttre div för att spara en HTML-variabel med namnet hasContent:

    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>
    

    Observera att data-sly-test används, att HTL test-blocket är nyckel, att det båda anger en HTML-variabel och att det HTML-element som det är på återges/inte. Den baseras på resultatet av utvärderingen av HTML-uttrycket. Om värdet är "true" återges elementet HTML, annars återges det inte.

    Den här HTML-variabeln hasContent kan nu återanvändas för att villkorligt visa/dölja platshållaren.

  2. Uppdatera det villkorliga anropet till placeholderTemplate längst ned i filen med följande:

    code language-html
    <sly data-sly-call="${placeholderTemplate.placeholder @ isEmpty=!hasContent}"></sly>
    

Visa bilden med hjälp av kärnkomponenter using-the-core-components-image

HTML-skriptet för byline.html är nu nästan färdigt och saknar bara bilden.

När sling:resourceSuperType pekar på kärnkomponentens Image-komponent för att skapa bilden, kan kärnkomponentens Image-komponent användas för att återge bilden.

För detta ska vi ta med den aktuella bylineresursen, men tvinga resurstypen för kärnkomponentens avbildningskomponent med resurstypen core/wcm/components/image/v2/image. Det här är ett kraftfullt mönster för återanvändning av komponenter. För detta används HTL:s data-sly-resource-block.

  1. Ersätt div med klassen cmp-byline__image med följande:

    code language-html
    <div class="cmp-byline__image"
        data-sly-resource="${ '.' @ resourceType = 'core/wcm/components/image/v2/image' }"></div>
    

    Denna data-sly-resource innehåller den aktuella resursen via den relativa sökvägen '.' och tvingar den aktuella resursen (eller den ursprungliga innehållsresursen) att inkluderas med resurstypen core/wcm/components/image/v2/image.

    Core Component-resurstypen används direkt, inte via en proxy, eftersom detta är en skriptbaserad användning som aldrig bevaras i innehållet.

  2. Slutförd byline.html nedan:

    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>
    
  3. Distribuera kodbasen till en lokal AEM. Eftersom ändringar gjordes i core och ui.apps måste båda modulerna distribueras.

    code language-shell
    $ cd aem-guides-wknd/ui.apps
    $ mvn clean install -PautoInstallPackage
    
    code language-shell
    $ cd ../core
    $ mvn clean install -PautoInstallBundle
    

    Anropa profilen classic om du vill distribuera till AEM 6.5/6.4:

    code language-shell
    $ cd ../core
    $ mvn clean install -PautoInstallBundle -Pclassic
    
    note caution
    CAUTION
    Du kan också skapa hela projektet från roten med hjälp av Maven-profilen autoInstallSinglePackage, men detta kan skriva över innehållsändringarna på sidan. Detta beror på att ui.content/src/main/content/META-INF/vault/filter.xml har ändrats för att självstudiekursens startkod ska skriva över det befintliga AEM. I ett verkligt scenario är detta inte något problem.

Granska den oformaterade Byline-komponenten reviewing-the-unstyled-byline-component

  1. När du har distribuerat uppdateringen navigerar du till sidan Ultimate Guide till LA Skateparks eller där du lade till Byline-komponenten tidigare i kapitlet.

  2. bild, namn och funktioner visas nu och har en ogiltig formatering, men det finns en fungerande Byline-komponent.

    Informningskomponent som inte är formaterad

Granska registreringen av försäljningsmodellen reviewing-the-sling-model-registration

Statusvyn 🔗 för AEM Web Console-modulen Sling Models visar alla registrerade Sling Models i AEM. Byline Sling Model kan valideras som installerad och identifieras genom att läsa den här listan.

Om BylineImpl inte visas i den här listan är det troligtvis ett problem med Sling-modellens anteckningar eller så har modellen inte lagts till i rätt paket (com.adobe.aem.guides.wknd.core.models) i huvudprojektet.

Byline Sling Model registrerad

http://localhost:4502/system/console/status-slingmodels

Format för pyline byline-styles

Om du vill justera den inbyggda komponenten mot den medföljande kreativa designen formaterar vi den. Detta uppnås genom att använda SCSS-filen och uppdatera filen i modulen ui.front.

Lägga till ett standardformat

Lägg till standardstilar för komponenten Byline.

  1. Återgå till IDE och projektet ui.front under /src/main/webpack/components:

  2. Skapa en fil med namnet _byline.scss.

    Byline project explorer

  3. Lägg till CSS för Byline-implementeringar (skrivet som SCSS) i _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;
        }
    }
    
  4. Öppna en terminal och navigera till modulen ui.frontend.

  5. Starta watch-processen med följande npm-kommando:

    code language-shell
    $ cd ui.frontend/
    $ npm run watch
    
  6. Återgå till webbläsaren och gå till LA SkateParks-artikeln. Du bör se de uppdaterade formaten för komponenten.

    har slutfört den inbyggda komponenten

    note tip
    TIP
    Du kan behöva rensa webbläsarens cacheminne för att vara säker på att inaktuell CSS inte hanteras, och uppdatera sidan med den inbyggda komponenten för att få den fullständiga formateringen.

Grattis! congratulations

Grattis! Du har skapat en egen komponent från grunden med Adobe Experience Manager!

Nästa steg next-steps

Fortsätt att lära dig mer om AEM komponentutveckling genom att utforska hur du skriver JUnit-tester för Byline Java™-koden för att säkerställa att allt utvecklas på rätt sätt och att implementerad affärslogik är korrekt och fullständig.

Visa den färdiga koden på GitHub eller granska och distribuera koden lokalt på Git-grenen tutorial/custom-component-solution.

  1. Klona github.com/adobe/aem-guides-wknd-databasen.
  2. Kolla in grenen tutorial/custom-component-solution
recommendation-more-help
b2a561c1-47c0-4182-b8c1-757a197484f9